SQL Server query to get date and naming columns by years - sql-server

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

Related

SQL Server Pivot on Multiple columns and dynamic column names

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

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;

get count of columns that have a -1, 0, 1, 2 values for a single row

Using SQL Server, I need to get a column count that has -1, 0, 1, 2 for its values for each row. There are different number of columns per table (one table has 55 fields)
-1 value = no answer or unknown (unknown_count)
0 value = ok (ok_count)
1 value = bad (bad_count)
2 value = not applicable (na_count)
row 1 looks like this
rowid name field_1 field_2 field_3 field_4
1 line_1 -1 1 2 1
2 line_2 2 1 -1 0
etc...
Results i would like to see
rowid na_count ok_count bad_count unknown_count
1 1 0 2 1
2 1 1 1 1
Extra credit question.... i need to get a count of all the fields that are used to build the counts (so i can give a percentile report)
You need a combination of UNPIVOT and some aggregation. Unpivot the data to a more sensible form, and then group by rowid summing up the various values:
with data AS
(
select
rowid,
field,
value
from
( SELECT rowid, field_1,field_2,field_3,field_4
FROM MyTable) p
UNPIVOT
( value FOR field IN (field_1,field_2,field_3,field_4) ) as unpvt
)
SELECT
rowid,
SUM(CASE WHEN value = 2 THEN 1 ELSE 0 END) AS na_count,
SUM(CASE WHEN value = 0 THEN 1 ELSE 0 END) AS ok_count,
SUM(CASE WHEN value = 1 THEN 1 ELSE 0 END) AS bad_count,
SUM(CASE WHEN value = -1 THEN 1 ELSE 0 END) AS unknown_count
from data
group by rowId
Live example: http://www.sqlfiddle.com/#!6/b702c/1
Building on the answer by Jamiec and some code I've used before; If you don't want to type in all the column names in the query you can use dynamic sql to build the query like this:
declare #tab nvarchar(max)
set #tab = N'your_table' -- change to your table name
declare #cols nvarchar(max)
select #cols = coalesce(#cols+N',', N'') + quotename(c.name) from syscolumns c
inner join sysobjects o on c.id = o.id and o.xtype = 'u'
where o.name = #tab
and c.name not in ('rowid', 'name') -- exclude the columns that don't hold data values
order by c.colid
declare #sql nvarchar(max)
select #sql = N'
select
rowid,
sum(case when val = 2 then 1 else 0 end) as ''na_count'',
sum(case when val = 0 then 1 else 0 end) as ''ok_count'',
sum(case when val = 1 then 1 else 0 end) as ''bad_count'',
sum(case when val = -1 then 1 else 0 end) as ''unknown_count'',
count(*) as column_count
from (select rowid, ' + #cols + N' from ' + #tab + N') as src
unpivot (val for col in (' + #cols + N')) as unpvt
group by rowid'
exec sp_executesql #sql
I think it will be more effective to sum up all the columns in a dynamical script, rather than to use unpivot. On one million rows i get 16% vs 84% relative to the batch in the execution plan (this codeĀ“s output #query vs the unpivot code).
You can use the same logic to get the percentages of each column. Please let me know if you want me to provide you with code for that as well.
--DROP TABLE TMP_Test
CREATE TABLE TMP_Test
(
rowid INT PRIMARY KEY IDENTITY(1,1)
, name varchar(10)
, field_1 INT
, field_2 INT
, field_3 INT
, field_4 INT
)
INSERT INTO TMP_Test
SELECT name = 'line_1', field_1 = -1, field_2=1, field_3=2, field_4=1
UNION ALL
SELECT name = 'line_2', field_1 = 2, field_2=1, field_3=-1, field_4= 0
/*
WHILE((SELECT COUNT(*) FROM TMP_Test) < 1000000)
BEGIN
INSERT INTO TMP_Test
SELECT name, field_1, field_2, field_3, field_4 FROM TMP_Test
END
*/
GO
DECLARE #query VARCHAR(MAX) = '';
DECLARE #schema VARCHAR(128) = 'dbo';
DECLARE #table VARCHAR(128) = 'TMP_Test';
DECLARE #na_count VARCHAR(max) = '';
DECLARE #ok_count VARCHAR(max) = '';
DECLARE #bad_count VARCHAR(max) = '';
DECLARE #unknown_count VARCHAR(max) = '';
SELECT
#na_count = #na_count + IIF(#na_count = '', '', ' + ') + 'IIF(' + COLUMN_NAME + ' = 2, 1, 0)'
, #ok_count = #ok_count + IIF(#ok_count = '', '', ' + ') + 'IIF(' + COLUMN_NAME + ' = 0, 1, 0)'
, #bad_count = #bad_count + IIF(#bad_count = '', '', ' + ') + 'IIF(' + COLUMN_NAME + ' = 1, 1, 0)'
, #unknown_count = #unknown_count + IIF(#unknown_count = '', '', ' + ') + 'IIF(' + COLUMN_NAME + ' = -1, 1, 0)'
FROM INFORMATION_SCHEMA.COLUMNS WHERE
TABLE_SCHEMA = #schema
AND TABLE_NAME = #table
AND COLUMN_NAME NOT IN ('rowid', 'name')
ORDER BY ORDINAL_POSITION;
SET #query = '
SELECT
rowid
, na_count = ' + #na_count + '
, ok_count = ' + #ok_count + '
, bad_count = ' + #bad_count + '
, unknown_count = ' + #unknown_count + '
FROM [' + #schema + '].[' + #table + ']';
PRINT(#query);
EXEC(#query);

Splitting Column wise values into rows

I have a table with year and month wise values at column level.
Table Structure
----------------
Franchise Year Month1 Month2 Month3 Month4 ....Month12
Need to fetch values of last six month values from current month through SQL. Can anybody help?
Eg. If I need last six months, It should be current year record with month2 and month1 values as well as 2013 record with Month12, Month11, Month10, Month9 values.
DECLARE #MonthStart date
DECLARE #yyyy char(4)
DECLARE #mmm char(3)
DECLARE #monthColumn varchar(7)
DECLARE #SQL nvarchar(max)
set #MonthStart = DATEADD(day,1-DAY(getdate()), getdate())
set #MonthStart = DATEADD(month,-6,#MonthStart )
SET #SQL = 'SELECT Franchise'
WHILE DATEDIFF(month,#MonthStart,GETDATE() )>0
BEGIN
SET #yyyy = DATENAME(yyyy,#MonthStart )
SET #mmm = DATENAME(mm,#MonthStart )
SET #monthColumn = 'Month'+convert(varchar(2),DATEpart(m,#MonthStart ))
SET #SQL = #SQL +',
SUM(CASE WHEN Year = '+#yyyy+' THEN ['+#monthColumn +'] ELSE NULL END) AS ['+#mmm+' '+#yyyy +']'
set #MonthStart = DATEADD(month,1,#MonthStart )
END
/*Substitute with the name of the table*/
SET #SQL = #SQL +'
FROM [TableName] '
/*For demonstration purposes show the SQL to be executes*/
PRINT #SQL
/*Try to Execute it */
EXEC (#SQL)
This will generate and execute a statement along the lines of
SELECT Franchise,
SUM(CASE WHEN Year = 2013 THEN [Month8] ELSE NULL END) AS [Aug 2013],
SUM(CASE WHEN Year = 2013 THEN [Month9] ELSE NULL END) AS [Sep 2013],
SUM(CASE WHEN Year = 2013 THEN [Month10] ELSE NULL END) AS [Oct 2013],
SUM(CASE WHEN Year = 2013 THEN [Month11] ELSE NULL END) AS [Nov 2013],
SUM(CASE WHEN Year = 2013 THEN [Month12] ELSE NULL END) AS [Dec 2013],
SUM(CASE WHEN Year = 2014 THEN [Month1] ELSE NULL END) AS [Jan 2014]
FROM [TableName]

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