I've tried to use information from similar subject but still can't cope with it.
I've got the data something like this:
+------------+--------------+--------+--------+------------+
| date | business_day | total | number | number_2 |
+------------+--------------+--------+--------+------------+
| 2018-04-03 | 1 | 325857 | 0 | 888 |
| 2018-04-04 | 2 | 196308 | 2 | 829 |
| 2018-04-05 | 3 | 434970 | 1 | 661 |
| 2018-04-06 | 4 | 665941 | 1 | 1135 |
| 2018-04-09 | 5 | 589334 | 0 | 837 |
+------------+--------------+--------+--------+------------+
How can I transpose it when number of rows could be dynamic (inthis case columns no), the final result should be like this:
+--------------+------------+------------+------------+------------+--------------+
| date | 2018-04-03 | 2018-04-04 | 2018-04-05 | 2018-04-06 | 2018-04-09 |
+--------------+------------+------------+------------+------------+--------------+
| business_day | 1 | 2 | 3 | 4 | 5 |
| total | 325857 | 196308 | 434970 | 665941 | 589334 |
| number | 0 | 2 | 1 | 1 | 0 |
| number_2 | 888 | 829 | 661 | 1135 | 837 |
+--------------+------------+------------+------------+------------+--------------+
Could you help me please?
If you need a dynamic query that automatically creates all the columns for all Date values you can use dynamic SQL, but be aware of security problems (SQL injection etc.) of this approach!
Here is a sample script that dynamically generates a query with all the columns for your Date:
if OBJECT_ID('Test') is not null
drop table [dbo].[Test]
CREATE TABLE [dbo].[Test]([date] datetime, business_day int, total int, number int, number_2 int)
insert into [dbo].[Test] select '2018-04-03', 1, 325857, 0, 888
insert into [dbo].[Test] select '2018-04-04', 2, 196308, 2, 829
insert into [dbo].[Test] select '2018-04-05', 3, 434970, 1, 661
insert into [dbo].[Test] select '2018-04-06', 4, 665941, 1, 1135
insert into [dbo].[Test] select '2018-04-09', 5, 589334, 0, 837
declare #dates nvarchar(max)='' --this variable holds all the dates that will become column names
declare #sql nvarchar(max)='' --this variable contains the TSQL dinamically generated
select #dates = #dates + ', [' + CONVERT(char(10), [date],126)+ ']' from [dbo].[Test]
set #dates = RIGHT(#dates, len(#dates)-2)
set #sql = #sql + 'select col as [date], ' + #dates
set #sql = #sql + 'from '
set #sql = #sql + '( '
set #sql = #sql + ' select [date] , col,val,ord '
set #sql = #sql + ' from [dbo].[Test] '
set #sql = #sql + ' CROSS APPLY ('
set #sql = #sql + ' VALUES (''business_day'',business_day, 1), '
set #sql = #sql + ' (''total'',total, 2), '
set #sql = #sql + ' (''number'',number, 3), '
set #sql = #sql + ' (''number_2'',number_2, 4) '
set #sql = #sql + ' )CS (COL,VAL,ORD) '
set #sql = #sql + ') src '
set #sql = #sql + 'pivot ( max(val) for [Date] in (' + #dates + ') ) piv '
set #sql = #sql + 'order by ord'
exec(#sql)
Here is the result of this statement:
Related
I have a pivoted SQL query that will bring up a table. But I need to format the dynamic column names and the results.
This is the query:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
DECLARE #yearV varchar(max) = '2019'
DECLARE #idclienteV varchar(max) = 1
select #cols = STUFF(
(
SELECT ',' + QUOTENAME([Y].[FechaPpto])
FROM [dbo].[Ingresos_Ppto] [Y]
WHERE
([Y].[IdCliente] = #idclienteV) AND (DATEPART(YEAR, [Y].[FechaPpto]) = #yearV)
GROUP BY [Y].[FechaPpto]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #query = N'DECLARE #year int = ' + #yearV + ' DECLARE #idc int = ' + #idclienteV + ' SELECT Rubro, ' + #cols + N' FROM
(
SELECT [Y].[Rubro], [Y].[Valor], [Y].[FechaPpto]
FROM [Ingresos_Ppto] [Y]
WHERE
([Y].[IdCliente] = #idc)
AND
(DATEPART(YEAR, [Y].[FechaPpto]) = #year)
) X
PIVOT
(
max(Valor)
FOR FechaPpto in (' + #cols + N')
) p'
EXEC sp_executesql #query;
And this is the table that it'll bring:
Rubro | Jan 1 2019 12:00 AM | Feb 1 2019 12:00 AM | Mar 1 2019 12:00 AM
+--------------------------+---------------------+---------------------+----------------------
Branding | 3280937.00 | 4912017.00 | 2404802.00
+--------------------------+---------------------+---------------------+----------------------
Mercadeo y publicidad | 3372619.00 | 7423175.00 | 8736550.00
+--------------------------+---------------------+---------------------+----------------------
Portal web WP | 9148489.00 | 9172295.00 | 4643597.00
+--------------------------+---------------------+---------------------+----------------------
Portal web WP + ecommerce | 3785304.00 | 9140700.00 | 7425106.00
+--------------------------+---------------------+---------------------+----------------------
Renting tecnológico | 7223406.00 | 7298693.00 | 8768783.00
+--------------------------+---------------------+---------------------+----------------------
So I need to change the dates column name and the result, to get this:
Rubro | Januray 2019 | February 2019 | March 2019
+--------------------------+---------------------+---------------------+----------------------
Branding | $3,280,937.00 | $4,912,017.00 | $2,404,802.00
+--------------------------+---------------------+---------------------+----------------------
Mercadeo y publicidad | $3,372,619.00 | $7,423,175.00 | $8,736,550.00
+--------------------------+---------------------+---------------------+----------------------
Portal web WP | $9,148,489.00 | $9,172,295.00 | $4,643,597.00
+--------------------------+---------------------+---------------------+----------------------
Portal web WP + ecommerce | $3,785,304.00 | $9,140,700.00 | $7,425,106.00
+--------------------------+---------------------+---------------------+----------------------
Renting tecnológico | $7,223,406.00 | $7,298,693.00 | $8,768,783.00
+--------------------------+---------------------+---------------------+----------------------
So far this is what I've tried:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
DECLARE #yearV varchar(max) = '2019'
DECLARE #idclienteV varchar(max) = 1
select #cols = STUFF(
(
SELECT ',' + QUOTENAME(FORMAT(dateadd(month, 0, [Y].[FechaPpto]), 'MMM', 'es-co'))
FROM [dbo].[Ingresos_Ppto] [Y]
WHERE
([Y].[IdCliente] = #idclienteV) AND (DATEPART(YEAR, [Y].[FechaPpto]) = #yearV)
GROUP BY FORMAT(dateadd(month, 0, [Y].[FechaPpto]), 'MMM', 'es-co')
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #query = N'DECLARE #year int = ' + #yearV + ' DECLARE #idc int = ' + #idclienteV + ' SELECT Rubro, ' + #cols + N' FROM
(
SELECT [Y].[Rubro], [Y].[Valor], [Y].[FechaPpto]
FROM [Ingresos_Ppto] [Y]
WHERE
([Y].[IdCliente] = #idc)
AND
(DATEPART(YEAR, [Y].[FechaPpto]) = #year)
) X
PIVOT
(
max(Valor)
FOR FORMAT(dateadd(month, 0, [Y].[FechaPpto]), 'MMM', 'es-co') in (' + #cols + N')
) p'
EXEC sp_executesql #query;
But I'm getting multiple syntax errors.
How can I format that result?
You need to double the single-quotes in your query string:
FOR FORMAT(dateadd(month, 0, [Y].[FechaPpto]), 'MMM', 'es-co') in
Needs to be:
FOR FORMAT(dateadd(month, 0, [Y].[FechaPpto]), ''MMM'', ''es-co'') in
etc
I have a table name table1
Partnumber | Model
12345 | A1
12345 | B2
12345 | C3
I would like my output to be:
Partnumber | Model1 | Model2 | Model3
12345 | A1 | B2 | C3
With the model columns N being dynamic.
Any ideas?
You can use dynamic conditional aggregation:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql =
'SELECT
PartNumber' + CHAR(10) +
STUFF((
SELECT DISTINCT
' ,MAX(CASE WHEN Model = ''' + Model + ''' THEN Model END) AS ' + QUOTENAME(Model) + CHAR(10)
FROM Tbl
FOR XML PATH('')
), 1, 0, '') +
'FROM Tbl
GROUP BY PartNumber;';
PRINT #sql;
EXEC sp_executesql #sql;
I have a table like
ID | TableID | FieldNames | Values
1 | 1 | FirstName | Value 1
2 | 1 | LastName | Value 2
3 | 1 | City | Value 3
4 | 2 | FirstName | Value 4
5 | 2 | LastName | Value 5
6 | 2 | City | Value 6
I need to show it in a grid like below
TableID | FirstName | LastName | City
1 | Value1 | Value2 | Value3
2 | Value4 | Value5 | value6
I know the concept PIVOT will help to get this implemented in MS SQL. But I need to implement this in Sybase. Field names are not fixed.
Do a 3-way self-join:
SELECT t1.Value as FirstName, t2.Value as LastName, t3.Value as City
FROM t t1, t t2, t t3
WHERE t1.ID + 1 = t2.ID
AND t1.ID +2 = t3.ID
With the help of various posts I have figured it out that I can do this using a dynamic case statement, like below.
DECLARE #query VARCHAR(800)
DECLARE #fieldName VARCHAR(255)
DECLARE #LoopCounter INT, #MaxCount INT
SET #query = 'select f.tableID, '
SET #fieldName = ''
SET #LoopCounter = 1
SET #MaxCount = (SELECT COUNT(DISTINCT FieldNames) FROM dbo.Table1)
WHILE (#LoopCounter <= #MaxCount)
BEGIN
SET #fieldName = (SELECT FieldNames FROM dbo.table1 WHERE ID= #LoopCounter)
SET #query = #query + ' max(case when f.FieldNames = ''' + #fieldName +''' then f.[Values] end) AS ''' + #fieldName + ''''
IF(#LoopCounter < #MaxCount)
BEGIN
SET #query = #query + ', '
END
SET #LoopCounter = #LoopCounter + 1
END
SET #query = #query + ' FROM dbo.table1 f GROUP BY f.TableID'
exec (#query)
I have a record in a single column.Like below
Address
Arulraj | Guindy | Chennai | TamilNaadu | India | 600042 | | 10000001
Adaiakalm | Chenanai | Chennai | TamilNaadu | India | 600042 | | 10000001
How to get the splitted string "TamilNaadu", "Chennai ","India " separately.
Here's a function that will split a string inline....
CREATE FUNCTION [dbo].[FN_SPLIT] ( --SELECT DBO.FN_SPLIT('TEST1 , TEST2', 2, ',')
#s varchar(512),
#i int,
#sep char(1) = ',')
RETURNS varchar(512)
AS
BEGIN
DECLARE #Ret VARCHAR(512);
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT #Ret =
RTRIM(SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END))
FROM Pieces
WHERE pn = #i
RETURN #Ret;
END
USAGE : SELECT DBO.FN_SPLIT('Address Arulraj | Guindy| Chennai | TamilNaadu | India | 600042 | | 10000001 ', 3, '|')
RETURNS : Chennai
Here's a table valued function that will return a table of values
CREATE FUNCTION [dbo].[FN_SPLIT_TBL](#InExp varchar(8000), #Sep varchar(10)) --SELECT * FROM DBO.[FN_SPLIT_TBL]('TEST1,TEST2', ',')
RETURNS #Res TABLE(
Pos int,
Value varchar(max))
AS
BEGIN
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#Sep, #InExp)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #InExp, stop + 1)
FROM Pieces
WHERE stop > 0
)
INSERT INTO #Res
SELECT pn, SUBSTRING(#InExp, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces OPTION (MAXRECURSION 0);
RETURN;
END
USAGE : SELECT *
FROM DBO.FN_SPLIT_TBL('Address Arulraj | Guindy| Chennai | TamilNaadu | India | 600042 | | 10000001 ', '|')
--WHERE Pos = 3 -- Uncomment this to only return index 3
RETURNS :
Pos Value
1 Address Arulraj
2 Guindy
3 Chennai
4 TamilNaadu
5 India
6 600042
7
8 10000001
i wrote a function for this, long time ago, nothing fancy, but sufficient:
-- =============================================
-- Author: Nico Boey
-- Create date:
-- Description: Returns the n'th part of a given string, split by a given separator, with n being a zero based index
-- =============================================
ALTER FUNCTION [dbo].[GetSplitStringPart]
(
#StringToSplit nvarchar(255)
, #Separator nvarchar(10)
, #Index int
)
RETURNS nvarchar(255)
AS
BEGIN
-- Declare the return variable here
DECLARE
#Result nvarchar(255)
, #SepPos int
, #PrevSepPos int
, #currentIndex int
if #Separator is null
select #Result = #StringToSplit
else if #StringToSplit is not null and #Index > -1
begin
-- init
select #currentIndex = 0, #PrevSepPos = 0
-- read ahead
select #SepPos = charindex(#Separator , #StringToSplit)
if #SepPos = 0 select #SepPos = len(#StringToSplit) + 1
-- loop until index is reached
while #currentIndex <= #Index
begin
if #currentIndex = #Index
begin
select #Result = substring(#StringToSplit, #PrevSepPos+1, #SepPos-#PrevSepPos-1)
end
select #currentIndex = #currentIndex + 1
select #PrevSepPos = #SepPos
if #PrevSepPos = len(#StringToSplit) + 1 break;
select #SepPos = charindex(#Separator , #StringToSplit, #PrevSepPos + 1)
if #SepPos = 0 select #SepPos = len(#StringToSplit) + 1
end
end
-- Return the result of the function
RETURN #Result
END
Try this if you are using Sql Server 2012+
DECLARE #str VARCHAR(1000)='Adaiakalm | Chenanai | Chennai | TamilNaadu | India | 600042 | | 10000001',
#sql NVARCHAR(max),
#index INT=3
SET #str = '''' + Replace(#str, '|', ''',''') + ''''
SET #sql= 'select choose('+ CONVERT(VARCHAR(10), #index) + ', ' + #str + ')'
EXEC Sp_executesql #sql
Try This:
DECLARE #str VARCHAR(1000)='Adaiakalm | Chenanai | Chennai | TamilNaadu | India | 600042 | | 10000001',
#part2 INT , #part3 INT , #part4 INT , #part5 INT ,#part6 INT = 1
set #part3 = CHARINDEX('|',#str,CHARINDEX('|',#str)+1)
set #part4 = CHARINDEX('|',#str,CHARINDEX('|',#str,CHARINDEX('|',#str)+1)+1)
set #part5 = CHARINDEX('|',#str,CHARINDEX('|',#str,CHARINDEX('|',#str,CHARINDEX('|',#str)+1)+1)+1)
set #part6 = CHARINDEX('|',#str,CHARINDEX('|',#str,CHARINDEX('|',#str,CHARINDEX('|',#str,CHARINDEX('|',#str)+1)+1)+1)+1)
select SUBSTRING(#str,#part3+1,#part4 - #part3-1) As "Part-3",SUBSTRING(#str,#part4+1,#part5 - #part4-1) As "Part-4",SUBSTRING(#str,#part5+1,#part6 - #part5-1) As "Part-5"
I have written a Dynamic Pivot Table Query based on the following. Here is a SQL FIDDLE for reference.
CREATE TABLE TestTable1 ([idnumber] INT, [DataTypeId] INT)
GO
INSERT INTO TestTable1
VALUES (1, 108), (1, 108), (1, 108), (2, 108),
(2, 108), (3, 108), (1, 109),(1, 109),
(1, 110),(2, 110),(1, 111),(4, 108),(4, 108),
(4, 110),(4, 111)
GO
Here is the Dynamic SQL that I wrote
DECLARE #SQL NVARCHAR(MAX),
#Cols NVARCHAR(MAX),
#ColsP NVARCHAR(MAX)
SELECT #Cols = STUFF((select ',
ISNULL([' + CAST([DataTypeId] as varchar(10)) + '], 0) AS ''' + CAST([DataTypeId] as varchar(10)) + ''''
FROM
(
SELECT [DataTypeId] FROM [TestTable1]
GROUP BY [DataTypeId]
HAVING [DataTypeId] <> ''
) AS d
ORDER BY [DataTypeId] FOR XML PATH(''),type).value('.','varchar(max)'),1,2,'')
-- /////////////THIS IS WHAT I WANT REMOVED ////////////////////
SELECT #ColsP = STUFF((select ',
[' + CAST([DataTypeId] as varchar(10)) + ']'
FROM
(
SELECT [DataTypeId] FROM [TestTable1]
GROUP BY [DataTypeId]
HAVING [DataTypeId] <> ''
) AS d
ORDER BY [DataTypeId] FOR XML PATH(''),type).value('.','varchar(max)'),1,2,'')
-- /////////////////////////////////////////////////////////////
SET #SQL = 'SELECT idnumber,' + #Cols + '
FROM
(SELECT idnumber, COUNT([DataTypeId]) AS Total, [DataTypeId] FROM [TestTable1]
GROUP BY idnumber, [DataTypeId]
HAVING [DataTypeId] <> ''''
) p
PIVOT
(
SUM(Total) FOR [DataTypeId] IN (' + #ColsP + ')
) AS pvt
ORDER BY pvt.idnumber'
-- print #SQL
EXECUTE( #SQL)
I get the result that I want:
| IDNUMBER | 108 | 109 | 110 | 111 |
|----------|-----|-----|-----|-----|
| 1 | 3 | 2 | 1 | 1 |
| 2 | 2 | 0 | 1 | 0 |
| 3 | 1 | 0 | 0 | 0 |
| 4 | 2 | 0 | 1 | 1 |
But I am sure it can be done better. I would like to remove where I populate the variable #ColsP - SELECT #ColsP = STUFF((select...")
There should be a way where I can create this dynamic code with just one loop through TestTable1. As you can see, I loop through it twice. Once to read what columns to create for the select statement, and once for the PIVOT table.
Here is the code that is generated by the Dynamic SQL:
SELECT idnumber,
ISNULL([108], 0) AS '108',
ISNULL([109], 0) AS '109',
ISNULL([110], 0) AS '110',
ISNULL([111], 0) AS '111'
FROM
(
SELECT idnumber, COUNT([DataTypeId]) AS Total, [DataTypeId]
FROM [TestTable2]
GROUP BY idnumber, [DataTypeId]
HAVING [DataTypeId] <> ''
) p
PIVOT
(
SUM(Total) FOR [DataTypeId] IN ([108], [109], [110], [111])
) AS pvt
ORDER BY pvt.idnumber
You can shorten your code considerably. First, you can just use count to aggregate the data in the PIVOT. There is no need for the inner count to aggregate the data or the HAVING clause. Finally, you only need to create the list of columns once. You could easily improve the code to:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(DataTypeId)
from TestTable1
group by DataTypeId
order by DataTypeId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= N'SELECT idnumber, ' + #cols + N'
from
(
select idnumber, DataTypeId
from TestTable1
) x
pivot
(
count(DataTypeId)
for DataTypeId in (' + #cols + N')
) p '
exec sp_executesql #query;
See SQL Fiddle with Demo. This gives the same result:
| IDNUMBER | 108 | 109 | 110 | 111 |
|----------|-----|-----|-----|-----|
| 1 | 3 | 2 | 1 | 1 |
| 2 | 2 | 0 | 1 | 0 |
| 3 | 1 | 0 | 0 | 0 |
| 4 | 2 | 0 | 1 | 1 |
Try replacing it with this.
SET NOCOUNT ON
IF OBJECT_ID('TestTable1') IS NOT NULL
DROP TABLE TestTable1
GO
CREATE TABLE TestTable1 ([idnumber] INT, [DataTypeId] INT)
GO
INSERT INTO TestTable1 VALUES
(1, 108),(1, 108),(1, 108),(2, 108),(2, 108),
(3, 108),(1, 109),(1, 109),(1, 110),(2, 110),
(1, 111),(4, 108),(4, 108),(4, 110),(4, 111)
DECLARE
#AllColumns NVARCHAR(MAX)
SELECT #AllColumns = ''
SELECT #AllColumns = #AllColumns +
'[' + CAST(DataTypeId as NVARCHAR)+'],'
FROM TestTable1
GROUP BY DataTypeId
SET #AllColumns = LEFT(#AllColumns,LEN(#AllColumns)-1)
PRINT #AllColumns