sample datas
date | num | name | value
01012012 | 1 | A | 20
01012012 | 1 | B | 30
01012012 | 2 | C | 40
wish to like this
date | num | A | B | C
01012012 | 1 | 20 | 30 | --
01012012 | 2 | -- | -- | 40
name is not fixed can allow many names
How about something like
DECLARE #Table TABLE(
date DATETIME,
num INT,
name VARCHAR(20),
value FLOAT
)
INSERT INTO #Table SELECT '20121201',1,'A',20
INSERT INTO #Table SELECT '20121201',1,'B',30
INSERT INTO #Table SELECT '20121201',2,'C',40
SELECT *
FROM (
SELECT date,
num,
name,
value
FROM #Table
) t
PIVOT (
SUM(Value) FOR name IN ([A],[B],[C])
) p
SQL Fiddle DEMO
For dynamic columns you need to use dynamic SQL
SQL FIDDLE EXAMPLE
declare
#cols nvarchar(max),
#stmt nvarchar(max)
select #cols = isnull(#cols + ', ', '') + '[' + Name + ']' from table1
select #stmt = '
select *
from table1 as T
pivot
(
max(T.value)
for name in (' + #cols + ')
) as P'
exec sp_executesql #stmt = #stmt
If you don't need a dynamic number of columns, you can use plain SQL like
select *
from table1 as T
pivot
(
max(T.value)
for name in ([A],[B],[C])
) as P
Related
I want a name to be passed as a parameter to a stored procedure and when i execute this stored procedure, it should divide the name into rows.
For example if pass 'Peter', output should be
P
e
t
e
r
The fastest method to do this would be with a Tally and SUBSTRING. I assume that the name won't be longer than 100 characters, and that (as it's a name) it's an nvarchar rather than a varchar. You can then do something like this:
DECLARE #Name nvarchar(100) = N'Peter';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP(DATALENGTH(#Name) / 2) --If a varchar, remove the / 2
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2)
SELECT SUBSTRING(#Name,T.I,1) AS C
FROM Tally T;
declare #input varchar(max);
set #input = 'Peter'
declare #table TABLE (char varchar(1));
while (LEN(#input)> 0)
begin
insert into #table select substring(#input,1,1)
select #input = RIGHT(#input,Len(#input)-1)
end
select * from #table
GO
| char |
| :--- |
| P |
| e |
| t |
| e |
| r |
declare #T table
(
ID int identity,
names varchar(10)
)
insert into #T
select 'Peter'
;with cte as
(
select ID,
left(names, 1) as Data,
stuff(names, 1, 1, '') as remains
from #T
where len(names) > 0
union all
select ID,
left(remains, 1) as Data,
stuff(remains, 1, 1, '') as remains
from cte
where len(remains) > 0
)
select ID,
Data
from cte
order by ID
ID | Data
-: | :---
1 | P
1 | e
1 | t
1 | e
1 | r
db<>fiddle here
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
Two Tables:
first Table name: employee_1 | Second table name:employee_2
code | Name | Salary | Month|Year code | Name | Salary |Post |Month |Year
-----+------+--------+------+-- -----+------+--------+-----+------+----
1 | A | 1000 | May | 2017 1 | A | 2000 |clerk|April |2017
2 | B | 1150 | May | 2017 2 | B | 3000 |clerk|April |2017
1 | A | 1200 | June | 2017
2 | B | 1150 | June | 2017
1 | A | 4000 | July | 2017
2 | B | 3500 | July | 2017
Output should be like this for year=2017:
code | Name |April | May | June | July
------+------+------+-------+------+-
1 | A |2000 | 1000 | 1200 | 4000
2 | B |3000 | 1150 | 1150 | 3500
I am not sure whether you can write an SQL query in one command for the output because here you want the row (Month) to be changed into the column. So the ideal solution will be writing a pivot query.
you can use the PIVOT function to transform the data from rows into columns.
It sounds like you will need to use dynamic SQL if the Months are unknown or if they dynamically changed.
create table employee (code int , name varchar(100), salary money, Month1 varchar(100));
INSERT INTO employee (code, name, salary, Month1)
VALUES
(1 , 'A' , 1000 , 'may'),
(2 , 'B' , 1150 , 'may'),
(1 , 'A' , 1200 , 'June'),
(2 , 'B' , 1150 , 'June'),
(1 , 'A' , 4000 , 'July'),
(2 , 'B' , 3500 , 'July')
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Month1)
from employee
group by Month1
order by Month1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
select #cols
set #query = 'SELECT code,name,' + #cols + ' from
(
select code, name, salary,Month1
from employee
) x
pivot
(
sum(salary)
for month1 in (' + #cols + ')
) p '
execute (#query)
#With Year as requested in comment
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#year As varchar(100)
select #cols = STUFF((SELECT ',' + QUOTENAME(Month1)
from employee1
group by Month1
order by Month1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #year = '2018'
set #query = 'SELECT code,name,' + #cols + ' from
(
select code, name, salary,Month1,year
from employee1
) x
pivot
(
sum(salary)
for month1 in (' + #cols + ')
) p where year ='+ #year +''
execute (#query)
with respect to two tables
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#year As varchar(100),
#cols1 as varchar(1000),
#cols2 as varchar(1000)
select #cols = STUFF((SELECT ',' + QUOTENAME(Month1)
from employee1
group by Month1
order by Month1
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #cols1 = STUFF((SELECT ',' + QUOTENAME(Month)
from employee2
group by Month
order by Month
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #cols2 = #cols1 +',' + #cols
set #year = '2017'
set #query = 'SELECT code,name,' + #cols2 + ' from
(
select code, name, salary,Month1,year
from employee1
union all
select code,name,salary,Month,year from employee2
) x
pivot
(
sum(salary)
for month1 in (' + #cols2 + ')
) p where year ='+ #year +''
execute (#query)
I found this great post for transposing a table in sql:
Simple way to transpose columns and rows in Sql?
edit:
input:
Paul | John | Tim | Eric
Red 'hi' | 5 | 1 | 3.3
Green 'there' | 4 | 3 | 5.5
Blue 'everyone'| 2 | 9 | 7.5
expected output:
Red | Green | Blue
Paul 'hi' | 'there' | 'everyone'
John 5 | 4 | 2
Tim 1 | 3 | 9
Eric 3.3 | 5.5 | 7.5
And I wanted to employ the last dynamic solution for a table that has different data types dynamically:
CREATE TABLE yourTable([color] nvarchar(5), [Paul] nvarchar(10), [John] int, [Tim]
int, [Eric] float);
INSERT INTO yourTable
([color], [Paul], [John], [Tim], [Eric])
VALUES
('Red', 'hi', 5, 1, 3.3),
('Green', 'there', 4, 3, 5.5),
('Blue', 'everyone', 2, 9, 7.5);
When I run the code from the previous answer:
DECLARE #colsUnpivot AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#colsPivot as NVARCHAR(MAX)
select #colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name <> 'color'
for xml path('')), 1, 1, '')
select #colsPivot = STUFF((SELECT ','
+ quotename(color)
from yourtable t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select name, '+#colsPivot+'
from
(
select color, name, value
from yourtable
unpivot
(
value for name in ('+#colsUnpivot+')
) unpiv
) src
pivot
(
sum(value)
for color in ('+#colsPivot+')
) piv'
exec(#query)
When I run this code I get the error message:
The type of column "John" conflicts with the type of other columns specified in the UNPIVOT list.
Is there a way that I can use this dynamic solution for my table without losing the dynamic nature of it? I'd like to ideally pass a bunch of tables into this method to transpose them in batch.
Thanks
A method to overcome this would be to use the data type SQL_VARIANT so that the resulting columns can handle more than a single data type. However you cannot SUM() SQL_VARIANT columns, so sum(value) has to be changed to max(value) - or min(value) - but for this pivot that change does not alter the result.
DECLARE #colsConvert AS NVARCHAR(MAX)
DECLARE #colsUnpivot AS NVARCHAR(MAX)
DECLARE #colsPivot as NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
select #colsConvert = (select ', cast('+quotename(C.name)+' as sql_variant) as '+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name <> 'color'
for xml path(''))
select #colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name <> 'color'
for xml path('')), 1, 1, '')
select #colsPivot = STUFF((SELECT ','
+ quotename(color)
from yourtable t
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query
= 'select name, '+#colsPivot+'
from
(
select color, name, value
from (select color'+#colsConvert+' from yourtable) as converted
unpivot
(
value for name in ('+#colsUnpivot+')
) unpiv
) src
pivot
(
max(value)
for color in ('+#colsPivot+')
) piv'
exec(#query)
See this working at: http://rextester.com/IBSN39688
Result:
+------+-----+-------+----------+
| name | Red | Green | Blue |
+------+-----+-------+----------+
| Eric | 3.3 | 5.5 | 7.5 |
| John | 5 | 4 | 2 |
| Paul | hi | there | everyone |
| Tim | 1 | 3 | 9 |
+------+-----+-------+----------+
The generated SQL:
select name, [Red],[Green],[Blue]
from
(
select color, name, value
from (select color, cast([Eric] as sql_variant) as [Eric], cast([John] as sql_variant) as [John], cast([Paul] as sql_variant) as [Paul], cast([Tim] as sql_variant) as [Tim] from yourtable) as converted
unpivot
(
value for name in ([Eric],[John],[Paul],[Tim])
) unpiv
) src
pivot
(
max(value)
for color in ([Red],[Green],[Blue])
) piv
+EDIT
An added benefit of using SQL_VARIANT in the result columns is that each standard data type encountered will adopt its default format. Particularly relevant for decimal/float and date/time data. You could also amend the defaults before running the dynamic pivot to further influence the output.
Demonstrated here
The following will transpose virtually any table, view, or query while respecting Row and Column sequences.
Full Disclosure: There is one major drawback. This approach does NOT handle NULL values well. A NULL value will cause the following columns to shift to the left.
Example
Declare #YourTable Table ([Color] varchar(50),[Paul] varchar(50),[John] int,[Tim] int,[Eric] decimal(10,1))
Insert Into #YourTable Values
('Red','hi',5,1,3.3)
,('Green','there',4,3,5.5)
,('Blue','everyone',2,9,7.5)
Declare #XML xml = (Select *,RowNr=Row_Number() over (Order By (Select NULL)) From #YourTable for XML RAW)
Select RowNr = Row_Number() over(Partition By r.value('#RowNr','int') Order By (Select null))
,ColNr = r.value('#RowNr','int')
,Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
Into #Temp
From #XML.nodes('/row') as XN(r)
Cross Apply XN.r.nodes('./#*') AS XA(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('RowNr')
Declare #SQL varchar(max) = '
Select [Item],' + Stuff((Select Distinct ',' + QuoteName(ColNr)+' as '+QuoteName(Value) From #Temp Where RowNr=1 Order by 1 For XML Path('')),1,1,'') + '
From #Temp
Pivot (max([Value]) For [ColNr] in (' + Stuff((Select Distinct ',' + QuoteName(ColNr) From #Temp Order by 1 For XML Path('')),1,1,'') + ') ) p
Where RowNr>1
Order By RowNr'
Exec(#SQL);
Returns
Item Red Green Blue
Paul hi there everyone
John 5 4 2
Tim 1 3 9
Eric 3.3 5.5 7.5
dbFiddle
There is a given table with following structure:
DateAndTime | Count Wine | Count Beer
2014-08-11 16:45:22.480 | 100 | 50
2014-08-12 16:45:22.480 | 50 | 50
2014-08-18 16:45:22.480 | 200 | 100
2014-08-19 16:45:22.480 | 300 | 200
What I need is a select statement with following Output:
--- | Week No 33 | Week No 34
Beer | 50 | 150
Wine | 75 | 250
So the columns (week no.) are dynamically depending on data.
And the calaculated values should be the average of the rows within the calendar week.
I have no idea how to solve this....
Not sure if it is an elegant way.. but this works.
create table sample
(
dtdate smalldatetime,
cWine int,
cBeer int
)
insert into sample
values('2014-08-11 16:45:22.480', 100 , 50),('2014-08-12 16:45:22.480', 50, 50),
('2014-08-18 16:45:22.480', 200 , 100),('2014-08-19 16:45:22.480', 300 , 200)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(DATEPART(WEEK,dtDate)) from sample FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'')
SELECT #query = 'SELECT Countname, '+#cols+'
FROM
(
SELECT countName,y,Avg(CountValue)avge from
(
SELECT countName,countValue,dtdate,DATEPART(WEEK,dtdate)as y from sample
unpivot(
countValue for Countname in (cwine,cBeer)
)unpiv )x group by y,countName
) x
pivot
(
sum(avge)
for y in ('+#cols+')
) p'
execute(#query)
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(DATEPART(WEEK,dtdate))
from master.sample
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #query = 'SELECT Countname, '+#cols+'
FROM
(
SELECT countName,y,Avg(CountValue)avge from
(
SELECT countName,countValue,dtdate,DATEPART(WEEK,dtdate)as y from sample
unpivot(
countValue for Countname in (cwine,cBeer)
)unpiv )x group by y,countName
) x
pivot
(
sum(avge)
for y in ('+#cols+')
) p'
SELECT #query
execute(#query)
I have a query result like this :
Date User1 User2 User3 ....
----------------------------------
1/1/2000 55 78 98 ...
1/1/2001 26 33 56 ...
1/1/2002 88 67 12 ...
The number of columns is not known because it is the result of a pivot query.
I would like to change the name the columns to something that looks like this :
Date User1 (blue) User2 (green) User3(brown)
The color is an information I retrieve from another table.
How can I achieve this ?
Thanks
Edit : Here is the query.
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(C.Name)
from [History]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT [Date],' + #cols +'
from
(
select [Date], Name, Value
from [History]
) x
pivot
(
max(value)
for Name in (' + #cols + ')
) p '
execute(#query)
SQL Fiddle
Schema Setup:
create table history (date datetime, name varchar(10), value int);
insert history values
('20130101', 'user1', 123),
('20130101', 'user2', 124),
('20130101', 'user3', 125),
('20130102', 'user1', 223),
('20130102', 'user3', 223),
('20130103', 'user2', 323);
create table colours (name varchar(10), colour_name varchar(10));
insert colours values
('user1', 'blue'),
('user2', 'green'),
('user3', 'brown');
Query:
DECLARE #scols nvarchar(max),
#cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = STUFF((
SELECT ',' + QUOTENAME(C.Name)
from (select distinct name from [History]) C
ORDER BY C.Name
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'');
select #scols = STUFF((
SELECT ',' + QUOTENAME(Name) + ' AS ' + QUOTENAME(colour_Name)
from (select distinct c.name, x.colour_name
from [History] C
JOIN colours x on x.name = c.name) y
ORDER BY Name
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'');
set #query = '
SELECT [Date],' + #scols +'
from (
select [Date], Name, Value
from [History]
) x
pivot
(
max(value)
for Name in (' + #cols + ')
) p ';
-- print #query --<< uncomment this line to see the query that gets generated
exec (#query);
Results:
| DATE | BLUE | GREEN | BROWN |
-------------------------------------------------------------
| January, 01 2013 00:00:00+0000 | 123 | 124 | 125 |
| January, 02 2013 00:00:00+0000 | 223 | (null) | 223 |
| January, 03 2013 00:00:00+0000 | (null) | 323 | (null) |
To get the mapping You can use a lookup table of old column name to new column name for example
CREATE TABLE colname(
oldname varchar(20),
newname varchar(20)
)
insert into colname values ( 'user1','user1 (blue)');
insert into colname values ( 'user2','user2 (green)');
then you can build an sql statement that uses this mapping
declare #sq varchar(2000)
set #sq ='select date'
select #sq = #sq + ',' + oldname + ' as [' + newname +']' from colname
set #sq = #sq + 'from ( existing query goes here ) '
select #sq
when the sql in #sq looks good you can replace the last select with
exec ( #sq )
to run the query
select Date, User1 as blue,User2 as green,User3 as brown from tableName
Use query like this.
Make use of 'as' keyword for changing column name.