SQL Server Pivot on Multiple columns and dynamic column names - sql-server

I am trying to pivot on multiple columns and have dynamic column names in the result. I am using SQL server 2014.
The original data looks like this
CREATE TABLE #s (grp varchar(3), id varchar(4), acc varchar(5), pr float, pos_live float, pos_yest float, fnd varchar(2))
INSERT INTO #s Values ('GR1','VX1','CFD01',25,100,95,'KY')
INSERT INTO #s Values ('GR1','VX1','UCD01',24.5,30,20,'UC')
INSERT INTO #s Values ('GR1','VX1','US1',25,10,95,'US')
INSERT INTO #s Values ('GR1','VX2','CFD01',20,10,10,'KY')
INSERT INTO #s Values ('GR1','VX2','UCD01',19,5,5,'UC')
INSERT INTO #s Values ('GR1','FVS1','CFD01',24,1,1,'KY')
INSERT INTO #s Values ('GR1','FVS1','UCD01',23,1,1,'UC')
INSERT INTO #s Values ('GR1','FVS1','EU1',23.5,1,1,'EU')
INSERT INTO #s Values ('GR2','FVS1','CFD02',24,10,10,'KY')
INSERT INTO #s Values ('GR2','FVS1','UCD02',23,10,10,'UC')
INSERT INTO #s Values ('GR2','FVS1','EU2',23.5,10,10,'EU')
And I would like to get this
I am struggling to use the pivot function on multiple columns and additionaly display dynamic column names in the result.

You can try to use condition aggregate function to make it, SUM with CASE WHEN
SELECT grp,
id,
SUM(CASE WHEN fnd = 'KY'THEN pr ELSE 0 END) pr_ky,
SUM(CASE WHEN fnd = 'UC'THEN pr ELSE 0 END) pr_uc,
SUM(CASE WHEN fnd = 'US'THEN pr ELSE 0 END) pr_us,
SUM(CASE WHEN fnd = 'EU'THEN pr ELSE 0 END) pr_eu,
SUM(CASE WHEN fnd = 'KY'THEN pos_live ELSE 0 END),
SUM(CASE WHEN fnd = 'UC'THEN pos_live ELSE 0 END),
SUM(CASE WHEN fnd = 'US'THEN pos_live ELSE 0 END),
SUM(CASE WHEN fnd = 'EU'THEN pos_live ELSE 0 END),
SUM(CASE WHEN fnd = 'KY'THEN pos_yest ELSE 0 END),
SUM(CASE WHEN fnd = 'UC'THEN pos_yest ELSE 0 END),
SUM(CASE WHEN fnd = 'US'THEN pos_yest ELSE 0 END),
SUM(CASE WHEN fnd = 'EU'THEN pos_yest ELSE 0 END)
FROM #s
GROUP BY grp,id
ORDER BY grp
sqlfiddle

You don't really want to use PIVOT here explicitly because it is meant to pivot one column, not multiple. Conditional aggregation like in #D-Shih's answer is the way you want to go, except you can't do that if you expect the query to change with the data. So you can use dynamic SQL:
DECLARE #s0 nvarchar(max) = N'',
#s1 nvarchar(max) = N'',
#s2 nvarchar(max) = N'',
#s3 nvarchar(max) = N'',
#sql nvarchar(max) = N'';
;WITH cols AS
(
SELECT fnd, efnd = char(39) + fnd + char(39) FROM #s
)
SELECT #s0 += N',
acc_' + fnd + N' '
+ N' = MAX(CASE fnd WHEN ' + efnd
+ N' THEN acc END)',
#s1 += N',
pr_' + fnd + N' '
+ N' = SUM(CASE fnd WHEN ' + efnd
+ N' THEN pr ELSE 0 END)',
#s2 += N',
pos_live_' + fnd
+ N' = SUM(CASE fnd WHEN ' + efnd
+ N' THEN pos_live ELSE 0 END)',
#s3 += N',
pos_yest_' + fnd
+ N' = SUM(CASE fnd WHEN ' + efnd
+ N' THEN pos_yest ELSE 0 END)'
FROM cols GROUP BY fnd, efnd;
SET #sql += N'SELECT grp, id' + #s0 + #s1 + #s2 + #s3 + N'
FROM #s GROUP BY grp, id ORDER BY grp;';
PRINT #sql;
EXEC sys.sp_executesql #sql;
Print output:
SELECT grp, id,
acc_EU = MAX(CASE fnd WHEN 'EU' THEN acc END),
acc_KY = MAX(CASE fnd WHEN 'KY' THEN acc END),
acc_UC = MAX(CASE fnd WHEN 'UC' THEN acc END),
acc_US = MAX(CASE fnd WHEN 'US' THEN acc END),
pr_EU = SUM(CASE fnd WHEN 'EU' THEN pr ELSE 0 END),
pr_KY = SUM(CASE fnd WHEN 'KY' THEN pr ELSE 0 END),
pr_UC = SUM(CASE fnd WHEN 'UC' THEN pr ELSE 0 END),
pr_US = SUM(CASE fnd WHEN 'US' THEN pr ELSE 0 END),
pos_live_EU = SUM(CASE fnd WHEN 'EU' THEN pos_live ELSE 0 END),
pos_live_KY = SUM(CASE fnd WHEN 'KY' THEN pos_live ELSE 0 END),
pos_live_UC = SUM(CASE fnd WHEN 'UC' THEN pos_live ELSE 0 END),
pos_live_US = SUM(CASE fnd WHEN 'US' THEN pos_live ELSE 0 END),
pos_yest_EU = SUM(CASE fnd WHEN 'EU' THEN pos_yest ELSE 0 END),
pos_yest_KY = SUM(CASE fnd WHEN 'KY' THEN pos_yest ELSE 0 END),
pos_yest_UC = SUM(CASE fnd WHEN 'UC' THEN pos_yest ELSE 0 END),
pos_yest_US = SUM(CASE fnd WHEN 'US' THEN pos_yest ELSE 0 END)
FROM #s GROUP BY grp, id ORDER BY grp;
Execution results:
grp
id
acc_EU
acc_KY
acc_UC
acc_US
pr_EU
pr_KY
pr_UC
pr_US
pos_live_EU
pos_live_KY
pos_live_UC
pos_live_US
pos_yest_EU
pos_yest_KY
pos_yest_UC
pos_yest_US
GR1
FVS1
EU1
CFD01
UCD01
null
23.5
24
23
0
1
1
1
0
1
1
1
0
GR1
VX1
null
CFD01
UCD01
US1
0
25
24.5
25
0
100
30
10
0
95
20
95
GR1
VX2
null
CFD01
UCD01
null
0
20
19
0
0
10
5
0
0
10
5
0
GR2
FVS1
EU2
CFD02
UCD02
null
23.5
24
23
0
10
10
10
0
10
10
10
0
Example db<>fiddle

Related

SQL Server query to get date and naming columns by years

I want my query to get the years and add it without me to added manually each year such as my below query.
Note that each columns should be named as it's years
USE [My_Table]
GO
DECLARE #max_intyear INT = 0 , #max_byteQuarter INT = 0
SELECT #max_intyear = MAX(intyear), #max_byteQuarter = MAX(byteQuarter)
FROM [My_Table].[dbo].[PW_Projected]
BEGIN
SELECT
intMedicine, intForm,
MAX(CASE WHEN intyear = #max_intyear and byteQuarter = #max_byteQuarter THEN curprice END) AS curprice,
MAX(CASE WHEN byteQuarter = 1 AND intYear = 2016 THEN [curUnits] END) AS '2016_Units(ID) Q1' ,
MAX(CASE WHEN byteQuarter = 2 AND intYear = 2016 THEN [curUnits] END) AS '2016_Units(ID) Q2',
MAX(CASE WHEN byteQuarter = 3 AND intYear = 2016 THEN [curUnits] END) AS '2016_Units(ID) Q3',
MAX(CASE WHEN byteQuarter = 4 AND intYear = 2016 THEN [curUnits] END) AS '2016_Units(ID) Q4',
MAX(CASE WHEN byteQuarter = 1 AND intYear = 2017 THEN [curUnits] END) AS '2017_Units(ID) Q1' ,
MAX(CASE WHEN byteQuarter = 2 AND intYear = 2017 THEN [curUnits] END) AS '2017_Units(ID) Q2',
MAX(CASE WHEN byteQuarter = 3 AND intYear = 2017 THEN [curUnits] END) AS '2017_Units(ID) Q3',
MAX(CASE WHEN byteQuarter = 4 AND intYear = 2017 THEN [curUnits] END) AS '2017_Units(ID) Q4'
INTO
Test
--intYear
FROM
dbo.PW_Projected
WHERE
intYear >= 2016
GROUP BY
intMedicine, intForm
END
You can generate dynamic sql, store it in a string variable (e.g. #SQL) and run it with sp_executesql #SQL.
Dynamic sql query could look like this (syntax errors could possibly occur, I didn't run it in SSMS):
declare #sql nvarchar(max) = N''
declare #YearQuarterSelection nvarchar(max) = N''
declare #YearQuarterTable table (
y int
, q int
)
-- get all years and quarters
insert into #YearQuarterTable (
y
, q
)
select
y = intYear
, q = byteQuarter
from dbo.PW_Projected
group by intYear, byteQuarter
-- generate "case when" conditions
select #YearQuarterSelection = N' MAX(CASE WHEN byteQuarter = ' +
cast (q as nvarchar(1)) +
N' AND intYear = ' +
cast (y as nvarchar(4))
N' THEN [curUnits] END) AS ''' +
cast (y as nvarchar(4)) +
N'_Units(ID) Q' +
cast (q as nvarchar(1)) +
N''','
from YearQuarterSelection
-- remove last comma
select #YearQuarterSelection = left(YearQuarterSelection, len(YearQuarterSelection) - 1)
-- build dynamic query
select #sql = N'SELECT intMedicine, intForm, MAX(CASE WHEN intyear = ' +
cast(#max_intyear as nvarchar(4)) +
N' and byteQuarter = ' +
cast(#max_byteQuarters nvarchar(1)) +
N' THEN curprice END) AS curprice, ' +
#YearQuarterSelection +
N'INTO
Test
--intYear
FROM
dbo.PW_Projected
WHERE
intYear >= 2016
GROUP BY
intMedicine, intForm'
-- run dynamic query
exec sp_executesql #sql
Best regards, Stephan

Dynamic SQL not getting correctly constructed at run time

I'm writing a dynamic query in SQL Server and coming across a problem I've never had before, the syntax is all correct - i've spent hours making sure it is but still the query is not giving the correct construction in the pivot section where selected columns are not being brought in completely when I check the query using a print statement.
Has anyone seen this before?
alter PROCEDURE [dbo].[spFinance_NegativePlugstest2]
#SnapshotPeriod VARCHAR(50) =NULL
, #Year INT
, #Aggs INT=1
AS
BEGIN
SET NOCOUNT ON;
DECLARE #owner AS NVARCHAR(MAX),
#ownermonths AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#SnapshotPeriod_Local AS VARCHAR(50)=#SnapshotPeriod
,#Year_Local INT =#Year,
#Aggs_Local INT =#Aggs;
IF #SnapshotPeriod_Local IS NULL
BEGIN
SELECT #SnapshotPeriod_Local= SnapshotPeriod
FROM vwFin_MainData c
WHERE IsCurrent=1
END
SET #owner = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.FinancialOwner + '_Neg')
FROM vwFin_MainData c
WHERE [Year]=#Year_Local
AND SnapshotPeriod=#SnapshotPeriod_Local
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #ownermonths = STUFF((SELECT DISTINCT ',' + QUOTENAME(c.OwnerMonth)
FROM (SELECT FinancialOwner + '_' + [Month] AS OwnerMonth FROM
(SELECT FinancialOwner, Jan AS Jan_Neg, Feb AS Feb_Neg , Mar AS Mar_Neg , Apr AS Apr_Neg , May AS May_Neg , Jun AS Jun_Neg, Jul AS Jul_Neg, Aug AS Aug_Neg, Sep AS Sep_Neg , Oct AS Oct_Neg, Nov AS Nov_Neg, Dec AS Dec_Neg
FROM vwFin_MainData c
WHERE [Year]=#Year_Local
AND SnapshotPeriod=#SnapshotPeriod_Local) p
UNPIVOT
(Monthly FOR [Month] IN
([Jan_Neg], [Feb_Neg], [Mar_Neg], [Apr_Neg], [May_Neg], [Jun_Neg], [Jul_Neg], [Aug_Neg], [Sep_Neg], [Oct_Neg], [Nov_Neg], [Dec_Neg])
) AS unpvt
)c FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #query ='; WITH _MainNegView AS (
SELECT
CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [PCMID]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN ''#Aggs''
END AS [PCMID]
, CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [PCMNAME]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +') =2 THEN ''#Aggs''
END AS [PCMNAME]
,[Year]
,[Tier1Name]
,[TIER2NAME]
,[TIER3NAME]
,[PARENTPROJECTID]
,[PARENTPROJECTNAME]
,SUM(CASE WHEN [Jan] > 0
THEN 0
ELSE [Jan]
END) AS [Jan_Neg]
,SUM(CASE WHEN [Feb] > 0
THEN 0
ELSE Feb
END) AS [Feb_Neg]
,SUM(CASE WHEN [Mar] > 0
THEN 0
ELSE [Mar]
END) AS [Mar_Neg]
,SUM(CASE WHEN [Apr] > 0
THEN 0
ELSE [Apr]
END) AS [Apr_Neg]
,SUM(CASE WHEN [May] > 0
THEN 0
ELSE [May]
END) AS [May_Neg]
,SUM(CASE WHEN [Jun] > 0
THEN 0
ELSE [Jun]
END) AS [Jun_Neg]
,SUM(CASE WHEN [Jul] > 0
THEN 0
ELSE [Jul]
END) AS [Jul_Neg]
,SUM(CASE WHEN [Aug] > 0
THEN 0
ELSE [Aug]
END) AS [Aug_Neg]
,SUM(CASE WHEN [Sep] > 0
THEN 0
ELSE [Sep]
END) AS [Sep_Neg]
,SUM(CASE WHEN [Oct] > 0
THEN 0
ELSE [Oct]
END) AS [Oct_Neg]
,SUM(CASE WHEN [Nov] > 0
THEN 0
ELSE [Nov]
END) AS [Nov_Neg]
,SUM(CASE WHEN [Dec] > 0
THEN 0
ELSE [Dec]
END) AS [Dec_Neg]
, SUM(CASE WHEN [Jan] > 0 THEN 0 ELSE [Jan] END +
CASE WHEN [Feb] > 0 THEN 0 ELSE [Feb] END +
CASE WHEN [Mar] > 0 THEN 0 ELSE [Mar] END +
CASE WHEN [Apr] > 0 THEN 0 ELSE [Apr] END +
CASE WHEN [May] > 0 THEN 0 ELSE [May] END +
CASE WHEN [Jun] > 0 THEN 0 ELSE [Jun] END +
CASE WHEN [Jul] > 0 THEN 0 ELSE [Jul] END +
CASE WHEN [Aug] > 0 THEN 0 ELSE [Aug] END +
CASE WHEN [Sep] > 0 THEN 0 ELSE [Sep] END +
CASE WHEN [Oct] > 0 THEN 0 ELSE [Oct] END +
CASE WHEN [Nov] > 0 THEN 0 ELSE [Nov] END +
CASE WHEN [Dec] > 0 THEN 0 ELSE [Dec] END) AS [FYF_Neg]
,[ProgramLOB]
,[Theme]
,[Closed-Completed]
, LastMonthActuals
, CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [ProjectLOB]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN ''#Aggs''
END AS [ProjectLOB]
, CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [ProjectLOBGroup]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN ''#Aggs''
END AS [ProjectLOBGroup]
, FinancialOwner AS FinancialOwner
FROM [dbo].[vwFin_MainData]
WHERE [Year] IN ('+ CAST(#Year_Local AS VARCHAR) +')
AND SnapshotPeriod IN (''' +#SnapshotPeriod_Local+ ''')
GROUP BY
CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [PCMID]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN ''#Aggs''
END
, CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [PCMNAME]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN ''#Aggs''
END
,[Year]
,[Tier1Name]
,[TIER2NAME]
,[TIER3NAME]
,[PARENTPROJECTID]
,[PARENTPROJECTNAME]
,[ProgramLOB]
,[Theme]
,[Closed-Completed]
, LastMonthActuals
, CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [ProjectLOB]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN ''#Aggs''
END
, CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [ProjectLOBGroup]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN ''#Aggs''
END
, FinancialOwner
) , _UnpivotedView AS (
SELECT
[ID]
, FinancialOwner
, [Month]
, FinancialOwner + ''_'' + [Month] AS OwnerMonth
, Monthly
FROM
(SELECT
CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [PCMID]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN [PARENTPROJECTID]
END AS [ID]
, FinancialOwner
, [Jan_Neg]
, [Feb_Neg]
, [Mar_Neg]
, [Apr_Neg]
, [May_Neg]
, [Jun_Neg]
, [Jul_Neg]
, [Aug_Neg]
, [Sep_Neg]
, [Oct_Neg]
, [Nov_Neg]
, [Dec_Neg]
FROM _MainNegView) p
UNPIVOT
(Monthly FOR [Month] IN
([Jan_Neg], [Feb_Neg], [Mar_Neg], [Apr_Neg], [May_Neg], [Jun_Neg], [Jul_Neg], [Aug_Neg], [Sep_Neg], [Oct_Neg], [Nov_Neg], [Dec_Neg])
) AS unpvt
SELECT
[ID]
,' + #ownermonths + ' FROM
(SELECT [ID]
,OwnerMonth
, Monthly
FROM _UnpivotedView
) BaseTable
PIVOT
(
SUM([Monthly])
FOR OwnerMonth in (' + #ownermonths + ')
)p '
print cast(#query as ntext)
--exec(#query)
END
GO
I found out what the issue was - the #query is an nvarchar(Max) , however within the query I pass in variables which I am casting to varchar but did not specify the length such as the following bit of code
, CASE WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=1 THEN [ProjectLOBGroup]
WHEN ('+ CAST(#Aggs_Local AS VARCHAR) +')=2 THEN ''#Aggs''
END
SQL was quietly truncating my #query string in the background. Casting them to NVARCHAR(MAX) solved the problem for me
, CASE WHEN ('+ CAST(#Aggs_Local AS NVARCHAR(MAX)) +')=1 THEN [ProjectLOBGroup]
WHEN ('+ CAST(#Aggs_Local AS NVARCHAR(MAX)) +')=2 THEN ''#Aggs''
END

What can I use in a SQL query to help me determine why my query is not returning any data results

Can someone assist me in troubleshooting my SQL query to discover why it's not returning any results, only the column aliases?
I've broken it apart, and all sections that obviously group together returns the expected data individually. Thanks for any guidance/assistance in advance. Below is my script:
...
DECLARE #u_cnt INT;
DECLARE #f_yr DATE;
DECLARE #qrt VARCHAR(3);
DECLARE #dnum VARCHAR(5);
SET #u_cnt = 10000;
SET #f_yr = '2002-05-20';
SET #qrt = 'Q2';
SET #dnum = '43234';
SELECT c.GroupLabel AS ORG_Code,
CONVERT (VARCHAR(7), FORMAT((CONVERT(DATE, c.changedate)), 'MM-yyyy')) AS [MONTH],
COUNT(DISTINCT CASE s.TestType
WHEN 'IR' THEN c.changedate
ELSE NULL END) AS TEST_DAYS,
COUNT(DISTINCT c.changedate) AS ALLDAYS,
COUNT(s.Id) AS total,
(CASE WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END) AS board_cnt,
FORMAT((COUNT(s.Id) / CASE
WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END), 'P0') AS pct_tested_text,
CASE WHEN 100 * (COUNT(s.Id) / CASE
WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END) >= 15
AND (COUNT(DISTINCT CASE s.TestType
WHEN 'IR' THEN c.changedate
ELSE NULL END)) >= 4
THEN 'Yes'
ELSE 'NO' END
FROM cforms c
INNER JOIN spitems sp
ON c.Id = s.FormId
WHERE c.Group = 'HR'
AND c.bFlag IS NULL
AND s.Report IN ('P', 'N')
AND CONVERT(VARCHAR(6), c.changedate, 112) IN
(SELECT
CASE
WHEN f.Quarter = 'Q1' THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + f.FyMonthNumber
WHEN f.Quarter = 'ALL' AND f.FyMonth IN ('OCT', 'NOV', 'DEC') THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1, 112) + f.FyM
ELSE CONVERT(VARCHAR(4), YEAR(#f_yr), 112) + f.FyM
END AS FY_MONTH
FROM fis f
WHERE f.Quarter = #qrt)
AND c.GroupLabel = 'Hr' + #dnum
GROUP BY c.GroupLabel, FORMAT((CONVERT(DATE, c.changedate)), 'MM-yyyy')
ORDER BY 1, FORMAT((CONVERT(DATE, c.changedate)), 'MM-yyyy');
Everything that could be limiting your data is in this part of your code below. I broke it apart and added comments to why and where they are limited. I think your CONVERT is the culprit.
--this inner join will limit the rows to only those with matching Id and FormId
INNER JOIN spitems sp
ON c.Id = s.FormId
--of the rows already filtered via the JOIN, they are further limited to thous with the Group = 'HR', a NULL bFlag, and Report = to P or N
WHERE c.Group = 'HR'
AND c.bFlag IS NULL
AND s.Report IN ('P', 'N')
--The first convert here changed changedate to yyyymmd (notice the day).
--In the sub-query, you seem to only be returning yyyymm formatted with a -,
--thus this would return ZERO rows. varchar(6) could resolve this,
--by dropping the day, but you'd need to add the hyphen or remove it from the `IN` clause
AND CONVERT(VARCHAR(7), c.changedate, 112) IN
(SELECT
CASE
WHEN f.Quarter = 'Q1' THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + '-' + f.FyMonthNumber
WHEN f.Quarter = 'ALL' AND f.FyMonth IN ('OCT', 'NOV', 'DEC') THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1, 112) + '-' + f.FyMonthNumber
ELSE CONVERT(VARCHAR(4), YEAR(#f_yr), 112) + '-' + f.FyMonthNumber
END AS FY_MONTH
FROM FyQm f
WHERE f.Quarter = #qrt)
--Lastly, there may be a case sensitivity here Hr vs HR or there just simply aren't any rows that match this predicate
AND c.GroupLabel = 'Hr' + #dnum
EDIT
Elaborating on my answer above... you have changed a portion of your where clause. Specifically the portion where you are evaluating c.changedate to a list of values. You have made the change to :
AND CONVERT(VARCHAR(6), c.changedate, 112) IN ...
This is a partial fix. It would remove the trailing DAY value you had before, leaving you with YYYYMM. However, in your subquery, you are formatting the list of values as YYYYMM-? where the ? is whatever f.FyMonthNumber is. As you can see, this will never match your original convert statement since it doesn't have a hyphen. The first thing to change would be remove the hyphen from the string concatenation. In your edited post, you have already done that so good job. Next, the issue could be that your + is not being treated as addition instead of concatenation when you are trying to combine it with f.FyMonthNumber. If f.FyMonthNumber is an int then it will add it.
DECLARE #f_yr DATE;
SET #f_yr = '2002-05-20';
SELECT CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + 02
Here you are wanting it to return 200102 but it returns 2003 since it's performing addition. You can cast it as a varchar or char to fix this.
SELECT CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + cast('02' as varchar)
Lastly, an issue you may run into is if f.FyMonthNumber is stored as an int, it won't have the leading zero. Thus, for January it would be represented as 1 instead of 01 and this would also return zero rows for any month before October. You can handle this with the right function.
DECLARE #f_yr DATE;
SET #f_yr = '2002-05-20';
SELECT CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + right('0' + cast('1' as varchar(2)),2) --a month as a single digit
SELECT CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + right('0' + cast('12' as varchar(2)),2) --a month with double digits
Putting that all together, I would suspect this edit would fix your issue. I would note though, you aren't evaluating any case expressions for Q2, Q3, or Q4 if that would be applicable...
DECLARE #u_cnt INT;
DECLARE #f_yr DATE;
DECLARE #qrt VARCHAR(3);
DECLARE #dnum VARCHAR(5);
SET #u_cnt = 10000;
SET #f_yr = '2002-05-20';
SET #qrt = 'Q2';
SET #dnum = '43234';
SELECT c.GroupLabel AS ORG_Code,
CONVERT (VARCHAR(7), FORMAT((CONVERT(DATE, c.changedate)), 'MM-yyyy')) AS [MONTH],
COUNT(DISTINCT CASE s.TestType
WHEN 'IR' THEN c.changedate
ELSE NULL END) AS TEST_DAYS,
COUNT(DISTINCT c.changedate) AS ALLDAYS,
COUNT(s.Id) AS total,
(CASE WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END) AS board_cnt,
FORMAT((COUNT(s.Id) / CASE
WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END), 'P0') AS pct_tested_text,
CASE WHEN 100 * (COUNT(s.Id) / CASE
WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END) >= 15
AND (COUNT(DISTINCT CASE s.TestType
WHEN 'IR' THEN c.changedate
ELSE NULL END)) >= 4
THEN 'Yes'
ELSE 'NO' END
FROM cforms c
INNER JOIN spitems sp
ON c.Id = s.FormId
WHERE c.Group = 'HR'
AND c.bFlag IS NULL
AND s.Report IN ('P', 'N')
AND CONVERT(VARCHAR(6), c.changedate, 112) IN
(SELECT
CASE
WHEN f.Quarter = 'Q1' THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + right('0' + cast(f.FyMonthNumber as varchar(2)))
WHEN f.Quarter = 'ALL' AND f.FyMonth IN ('OCT', 'NOV', 'DEC') THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1, 112) + right('0' + cast(f.FyMonthNumber as varchar(2)))
ELSE CONVERT(VARCHAR(4), YEAR(#f_yr), 112) + right('0' + cast(f.FyMonthNumber as varchar(2)))
END AS FY_MONTH
FROM fis f
WHERE f.Quarter = #qrt)
AND c.GroupLabel = 'Hr' + #dnum
GROUP BY c.GroupLabel, FORMAT((CONVERT(DATE, c.changedate)), 'MM-yyyy')
ORDER BY 1, FORMAT((CONVERT(DATE, c.changedate)), 'MM-yyyy');
Try to change to this (look at the 1st convert):
...
AND CONVERT(VARCHAR(7), c.changedate, 120) IN
(SELECT
CASE
WHEN f.Quarter = 'Q1' THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + '-' + f.FyMonthNumber
WHEN f.Quarter = 'ALL' AND f.FyMonth IN ('OCT', 'NOV', 'DEC') THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1, 112) + '-' + f.FyMonthNumber
ELSE CONVERT(VARCHAR(4), YEAR(#f_yr), 112) + '-' + f.FyMonthNumber
END AS FY_MONTH
FROM FyQm f
WHERE f.Quarter = #qrt)
...
You was converting 112 (yyyymm) instead of 120 (yyyy-mm) and your inner select returns yyyy-mm
JOIN or/and WHERE clauses can be a reason.
Following basic deduction method is to figure out which part of the query gives such result:
Firstly, eliminate all WHERE clauses and check if current JOIN can return rows by setting WHERE this way:
WHERE 1 = 1
--AND c.Group = 'HR'
--AND c.bFlag IS NULL
--AND s.Report IN ('P', 'N')
--AND CONVERT(VARCHAR(6), c.changedate, 112) IN
-- (SELECT
-- CASE
-- WHEN f.Quarter = 'Q1' THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + f.FyMonthNumber
-- WHEN f.Quarter = 'ALL' AND f.FyMonth IN ('OCT', 'NOV', 'DEC') THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1, 112) + f.FyM
-- ELSE CONVERT(VARCHAR(4), YEAR(#f_yr), 112) + f.FyM
-- END AS FY_MONTH
-- FROM fis f
-- WHERE f.Quarter = #qrt)
--AND c.GroupLabel = 'Hr' + #dnum
Then, uncomment WHERE statetements one by one to figure out which one filters rows:
WHERE 1 = 1
AND c.Group = 'HR'
--AND c.bFlag IS NULL
--AND s.Report IN ('P', 'N')
--AND CONVERT(VARCHAR(6), c.changedate, 112) IN
-- (SELECT
-- CASE
-- WHEN f.Quarter = 'Q1' THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + f.FyMonthNumber
-- WHEN f.Quarter = 'ALL' AND f.FyMonth IN ('OCT', 'NOV', 'DEC') THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1, 112) + f.FyM
-- ELSE CONVERT(VARCHAR(4), YEAR(#f_yr), 112) + f.FyM
-- END AS FY_MONTH
-- FROM fis f
-- WHERE f.Quarter = #qrt)
--AND c.GroupLabel = 'Hr' + #dnum
Then, another statement:
WHERE 1 = 1
AND c.Group = 'HR'
AND c.bFlag IS NULL
--AND s.Report IN ('P', 'N')
--AND CONVERT(VARCHAR(6), c.changedate, 112) IN
-- (SELECT
-- CASE
-- WHEN f.Quarter = 'Q1' THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1) + f.FyMonthNumber
-- WHEN f.Quarter = 'ALL' AND f.FyMonth IN ('OCT', 'NOV', 'DEC') THEN CONVERT(VARCHAR(4), YEAR(#f_yr) - 1, 112) + f.FyM
-- ELSE CONVERT(VARCHAR(4), YEAR(#f_yr), 112) + f.FyM
-- END AS FY_MONTH
-- FROM fis f
-- WHERE f.Quarter = #qrt)
--AND c.GroupLabel = 'Hr' + #dnum
And so on, until you get into the point when no rows returned
This technique will bring you eventually to a part (parts) of the query which filters rows out
If original dataset returns too big number of rows it can be expensive to retrieve all of them during the debugging, so I would recomend to comment them out and use COUNT(*) instead:
SELECT COUNT(*)
/*
c.GroupLabel AS ORG_Code,
CONVERT (VARCHAR(7), FORMAT((CONVERT(DATE, c.changedate)), 'MM-yyyy')) AS [MONTH],
COUNT(DISTINCT CASE s.TestType
WHEN 'IR' THEN c.changedate
ELSE NULL END) AS TEST_DAYS,
COUNT(DISTINCT c.changedate) AS ALLDAYS,
COUNT(s.Id) AS total,
(CASE WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END) AS board_cnt,
FORMAT((COUNT(s.Id) / CASE
WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END), 'P0') AS pct_tested_text,
CASE WHEN 100 * (COUNT(s.Id) / CASE
WHEN (#u_cnt IS NULL) THEN -1
ELSE #u_cnt
END) >= 15
AND (COUNT(DISTINCT CASE s.TestType
WHEN 'IR' THEN c.changedate
ELSE NULL END)) >= 4
THEN 'Yes'
ELSE 'NO' END
*/
FROM cforms c
I've reformated your code with remarks:
declare #u_cnt int, #f_yr date, #qrt varchar(3), #dnum varchar(5);
select #u_cnt = 10000, #f_yr = '20020520', #qrt = 'Q2', #dnum = '43234';
select c.GroupLabel as ORG_CODE
-- Assuming c.changedate is datetime, otherwise cast(c.changedate as datetime)
, format(c.changedate, 'MM-yyyy') as [MONTH]
, count(distinct case s.TestType when 'IR' then c.changedate else null end) as IR_TEST_DAYS
, count(distinct c.changedate) as TEST_DAYS
, count(s.Id) as TOTAL
, (case when (#u_cnt is null) then - 1 else #u_cnt end) as BOARD_CNT
, format((count(s.Id) /
-- avoiding also division by 0
case when isnull(#u_cnt, 0) = 0
then - 1
else #u_cnt end), 'P0')
as PCT
, case
when 100 * (count(s.Id) /
-- avoiding also division by 0
case when isnull(#u_cnt, 0) = 0
then - 1
else #u_cnt
end) >= 15
and (count(distinct case s.TestType
when 'IR' then c.changedate else null
end)) >= 4
then 'Yes' else 'NO'
end as PCT_TEST_COMP
from cforms c
join spitems s on (c.Id = s.FormId)
where c.group = 'HR'
and c.bFlag is null
and s.Report in ('P', 'N')
and convert(varchar(6), c.changedate, 112) in -- yyyymm (ISO format)
(
select
cast(year(#f_yr) +
case
when f.Quarter = 'Q1'
then (-1)
when f.Quarter = 'ALL'
and f.FyMonthNumber in ('10', '11', '12')
then (-1)
else (0)
end as varchar(4))
+ f.FyMonthNumber -- JAN = '01' or '1' ?
from FyQm f
where f.Quarter = #qrt
)
and c.GroupLabel = 'Hr' + #dnum
group by c.GroupLabel
-- Assuming c.changedate is datetime, otherwise cast(c.changedate as datetime)
, format(c.changedate, 'MM-yyyy')
order by ORG_CODE, [MONTH];
Could you check if FyQm.FyMonthNumber is varchar(2) or char(2) and represents January as '01' instead of '1'?
Your main question is why you are not getting data for given query ?
So you want to debug and check where the problem is.
So for given parameter,
DECLARE #u_cnt INT;
DECLARE #f_yr DATE;
DECLARE #qrt VARCHAR(3);
DECLARE #dnum VARCHAR(5);
SET #u_cnt = 10000;
SET #f_yr = '2002-05-20';
SET #qrt = 'Q2';
SET #dnum = '43234';
so start from basic
select *
FROM cforms c
--INNER JOIN spitems sp
--ON c.Id = s.FormId
WHERE c.Group = 'HR'
--AND c.bFlag IS NULL
--AND s.Report IN ('P', 'N')
note the comments part,does it return data, if yes then uncomment AND c.bFlag IS NULL and this way uncomments other part.
Are you sure it will be INNER JOIN or LEFt JOIN ?
Put the period subquery in temp table,though this is not main reason,if it return less records then you can use CTE also,
Create table #tempperiod(period varchar(6))
insert into #tempperiod(period)
select
cast(year(#f_yr) +
case
when f.Quarter = 'Q1'
then (-1)
when f.Quarter = 'ALL'
and f.FyMonthNumber in ('10', '11', '12')
then (-1)
else (0)
end as varchar(4))
+ f.FyMonthNumber
from FyQm f
where f.Quarter = #qrt
-- in order to test,does it return any records,does it return desire output
select * from #tempperiod
Check for space (LTRIM and RTRIM) in columns which is use in predicate.
Avoid division by 0 in your case statement
If it is really,INNER JOIN then use EXISTS clause coz you don't require spitems sp columns .
What is ORDER BY 1 ? GroupLabel ? then you don't need them in Order clause coz all rows will be 'HR'+'43234'
ABOVE all,you don't need Order by at all,coz Group By will sort it for you and that is the only requirement.
Thoroughly check #tempperiod data,is the format same as
CONVERT(VARCHAR(6), c.changedate, 112)
My bet is on different column definitions
DECLARE #qrt VARCHAR(3);
vs
whatever is FROM FyQm f WHERE f.Quarter = #qrt
'Q2 ' with a blank or null probably does not equal f.Quarter which may be defined as VARCHAR(2)
Perhaps we could try this sql to see that each criterion has some rows
Select
Sum(1) as cntAll
,Sum (CASE When c.Group = 'HR' Then 1 Else 0 End) as cntGroup
,Sum (CASE When c.bFlag IS NULL Then 1 Else 0 End) as cntbFlag
,Sum (CASE When s.Report IN ('P', 'N') Then 1 Else 0 End) as cntsReport
,Sum (CASE When CONVERT(VARCHAR(6), c.changedate, 112)
IN ('200204', '200205', '200206') Then 1 Else 0 End) as cntchangedate
,Sum (CASE When c.GroupLabel = 'Hr43234' Then 1 Else 0 End) as cntGroupLabel
FROM cforms c
INNER JOIN spitems s
ON c.Id = s.FormId
Maybe time to follow #scsimon advice, and add back criteria one at a time and see which one blocks all of the rows
-- WHERE c.Group = 'HR'
-- AND c.bFlag IS NULL
-- AND s.Report IN ('P', 'N')
-- AND CONVERT(VARCHAR(6), c.changedate, 112) IN ('200204', '200205', '200206')
-- AND c.GroupLabel = 'Hr43234'

SQL Server Row Into Column

We have a table showing amounts (CHGCNT) for 3 dates (5-9-2016, 5-10-2016, 5-11-2016) for each store & Depts.
I want to be able to see the records in a table like this:
I already applied the following query
declare #dt as datetime
declare #dt1 as varchar(10)
declare #dt2 as varchar(10)
declare #dt3 as varchar(10)
select distinct #dt = min(effdt) from [HQExtract].[dbo].[FSM_PRICE_TAGCOUNT]
-- print CONVERT(CHAR(10), #dt, 110) + ' ---- ' + CONVERT(CHAR(10), #dt+1 , 110)
set #dt1 = CONVERT(CHAR(10), #dt, 110)
set #dt2 = CONVERT(CHAR(10), #dt +1 , 110)
set #dt3 = CONVERT(CHAR(10), #dt + 2 , 110)
--print #dt1 + ' ---- ' + #dt2 + '-----' + #dt3
SELECT DEPTNM, DEPT, [#dt1] , [#dt2] , [#dt3]
FROM [HQExtract].[dbo].[FSM_PRICE_TAGCOUNT]
PIVOT
(
SUM(CHGCNT)
FOR effdt IN ( [#dt1] , [#dt2] , [#dt3])
) AS P
but it is returning dates
I like the SUM-CASE approach:
SELECT deptnm,
SUM(CASE WHEN effdt = '2016-05-09' THEN chgcnt ELSE 0 END) "2016-05-09",
SUM(CASE WHEN effdt = '2016-05-10' THEN chgcnt ELSE 0 END) "2016-05-10",
SUM(CASE WHEN effdt = '2016-05-11' THEN chgcnt ELSE 0 END) "2016-05-11",
SUM(effdt) Total
FROM [HQExtract].[dbo].[FSM_PRICE_TAGCOUNT]
GROUP BY deptnm;

how to include the parameter name as the name of a column in sql

I have a stored procedure that looks something like this :
CREATE PROCEDURE [dbo].[spRS_Get]
#Year Varchar(20),
#Month Varchar(20)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Paramdate datetime;
DECLARE #ParamYear varchar(4);
DECLARE #ParamMonth varchar(2);
DECLARE #Concatt varchar(10);
SET #ParamYear = #Year;
SET #ParamMonth = #Month;
SET #Concatt = #ParamMonth + '-' + '01' + '-' + #ParamYear;
SET #Paramdate = CONVERT(datetime, #Concatt);
SELECT Item
,SUM(CASE WHEN [MONTH] = month(#ParamDate) THEN Sales END) AS month(#ParamDate)
,SUM(CASE WHEN [MONTH] = month(#ParamDate) - 1 THEN Sales END) AS month(#ParamDate) - 1,
,SUM(CASE WHEN [MONTH] = month(#ParamDate) THEN Sales END) - SUM(CASE WHEN [MONTH] = month(#ParamDate) - 1 THEN Sales END) AS month(#ParamDate) - month(#ParamDate) - 1
FROM ABC
GROUP BY Item
In the above query , the part after the AS causes the error.
I want to use the parameter name as the name of the column , but it gives me an error. Is there a way I can use
the parameter name as the name of the month ?
You can only do that by building the select statement as a string and then executing it using the sp_executesql command.
So you'd get something like this:
declare #month0 varchar(2) = cast(month(#paramdate) as varchar(2));
declare #month1 varchar(2) = cast((month(#ParamDate) - 1) as varchar(2));
declare #month2 varchar(2) =
cast((month(#ParamDate) - month(#ParamDate) - 1) as varchar(2));
declare s nvarchar(1024) =
'SELECT Item
, SUM(CASE WHEN [MONTH] = month(#d) THEN Sales END)
AS ''' + #month0 +
''' , SUM(CASE WHEN [MONTH] = month(#d) - 1 THEN Sales END)
AS ''' + #month1 +
''' , SUM(CASE WHEN [MONTH] = month(#d) THEN Sales END) -
SUM(CASE WHEN [MONTH] = month(#d) - 1 THEN Sales END)
AS ''' + #month2 +
''' FROM ABC GROUP BY Item';
EXEC sp_executesql #s, N'#d DATETIME', #ParamDate;

Resources