Transpose a word written vertically to horizontally in SQL Server
up vote
3
down vote
favorite
I want to use T-SQL to transpose a column with a word, such as
t
r
a
i
n
into
train
I tried using pivot, but instead of getting only one row back with the transposed column, I got a 5x5 table showing 'train' along the diagonal, and NULL everywhere else. This result makes sense to me, but it's not what I want. I just want to transpose a word written vertically into the same word but written horizontally. How should I approach this with pivot? Or is there an easier way to do it otherwise?
Conversely, if I had instead started out with a word
train
how would I transpose this word to make it print vertically?
t
r
a
i
n
Thank you!
sql sql-server tsql transpose
|
show 1 more comment
up vote
3
down vote
favorite
I want to use T-SQL to transpose a column with a word, such as
t
r
a
i
n
into
train
I tried using pivot, but instead of getting only one row back with the transposed column, I got a 5x5 table showing 'train' along the diagonal, and NULL everywhere else. This result makes sense to me, but it's not what I want. I just want to transpose a word written vertically into the same word but written horizontally. How should I approach this with pivot? Or is there an easier way to do it otherwise?
Conversely, if I had instead started out with a word
train
how would I transpose this word to make it print vertically?
t
r
a
i
n
Thank you!
sql sql-server tsql transpose
2
What RDMS are you using? I deleted my answer because it was SQL Server specific and you only tagged [sql].
– Ross Bush
Nov 10 at 4:09
I'm using Microsoft SQL Server Management Studio.
– kkun
Nov 10 at 4:14
1
Whichever answer you go with for the first part of this question, please think about the query which returns one letter on each row...The solution is dependent upon the order. So if you use it in a real scenario, make sure you have anorder by
clause in the query which returns the letters; otherwise at some point in future, SQL might decide a different access path, returning your letters in a different order.
– Richardissimo
Nov 10 at 7:39
SQL Server 2017 has a function STRING_AGG() which is worth looking into if you're up to date with versions.
– e_i_pi
Nov 10 at 7:55
Is this a homework question? There would never be a valid real life scenario for this...
– theMayer
Nov 10 at 13:45
|
show 1 more comment
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I want to use T-SQL to transpose a column with a word, such as
t
r
a
i
n
into
train
I tried using pivot, but instead of getting only one row back with the transposed column, I got a 5x5 table showing 'train' along the diagonal, and NULL everywhere else. This result makes sense to me, but it's not what I want. I just want to transpose a word written vertically into the same word but written horizontally. How should I approach this with pivot? Or is there an easier way to do it otherwise?
Conversely, if I had instead started out with a word
train
how would I transpose this word to make it print vertically?
t
r
a
i
n
Thank you!
sql sql-server tsql transpose
I want to use T-SQL to transpose a column with a word, such as
t
r
a
i
n
into
train
I tried using pivot, but instead of getting only one row back with the transposed column, I got a 5x5 table showing 'train' along the diagonal, and NULL everywhere else. This result makes sense to me, but it's not what I want. I just want to transpose a word written vertically into the same word but written horizontally. How should I approach this with pivot? Or is there an easier way to do it otherwise?
Conversely, if I had instead started out with a word
train
how would I transpose this word to make it print vertically?
t
r
a
i
n
Thank you!
sql sql-server tsql transpose
sql sql-server tsql transpose
edited Nov 10 at 9:48
marc_s
567k12710961246
567k12710961246
asked Nov 10 at 3:50
kkun
253
253
2
What RDMS are you using? I deleted my answer because it was SQL Server specific and you only tagged [sql].
– Ross Bush
Nov 10 at 4:09
I'm using Microsoft SQL Server Management Studio.
– kkun
Nov 10 at 4:14
1
Whichever answer you go with for the first part of this question, please think about the query which returns one letter on each row...The solution is dependent upon the order. So if you use it in a real scenario, make sure you have anorder by
clause in the query which returns the letters; otherwise at some point in future, SQL might decide a different access path, returning your letters in a different order.
– Richardissimo
Nov 10 at 7:39
SQL Server 2017 has a function STRING_AGG() which is worth looking into if you're up to date with versions.
– e_i_pi
Nov 10 at 7:55
Is this a homework question? There would never be a valid real life scenario for this...
– theMayer
Nov 10 at 13:45
|
show 1 more comment
2
What RDMS are you using? I deleted my answer because it was SQL Server specific and you only tagged [sql].
– Ross Bush
Nov 10 at 4:09
I'm using Microsoft SQL Server Management Studio.
– kkun
Nov 10 at 4:14
1
Whichever answer you go with for the first part of this question, please think about the query which returns one letter on each row...The solution is dependent upon the order. So if you use it in a real scenario, make sure you have anorder by
clause in the query which returns the letters; otherwise at some point in future, SQL might decide a different access path, returning your letters in a different order.
– Richardissimo
Nov 10 at 7:39
SQL Server 2017 has a function STRING_AGG() which is worth looking into if you're up to date with versions.
– e_i_pi
Nov 10 at 7:55
Is this a homework question? There would never be a valid real life scenario for this...
– theMayer
Nov 10 at 13:45
2
2
What RDMS are you using? I deleted my answer because it was SQL Server specific and you only tagged [sql].
– Ross Bush
Nov 10 at 4:09
What RDMS are you using? I deleted my answer because it was SQL Server specific and you only tagged [sql].
– Ross Bush
Nov 10 at 4:09
I'm using Microsoft SQL Server Management Studio.
– kkun
Nov 10 at 4:14
I'm using Microsoft SQL Server Management Studio.
– kkun
Nov 10 at 4:14
1
1
Whichever answer you go with for the first part of this question, please think about the query which returns one letter on each row...The solution is dependent upon the order. So if you use it in a real scenario, make sure you have an
order by
clause in the query which returns the letters; otherwise at some point in future, SQL might decide a different access path, returning your letters in a different order.– Richardissimo
Nov 10 at 7:39
Whichever answer you go with for the first part of this question, please think about the query which returns one letter on each row...The solution is dependent upon the order. So if you use it in a real scenario, make sure you have an
order by
clause in the query which returns the letters; otherwise at some point in future, SQL might decide a different access path, returning your letters in a different order.– Richardissimo
Nov 10 at 7:39
SQL Server 2017 has a function STRING_AGG() which is worth looking into if you're up to date with versions.
– e_i_pi
Nov 10 at 7:55
SQL Server 2017 has a function STRING_AGG() which is worth looking into if you're up to date with versions.
– e_i_pi
Nov 10 at 7:55
Is this a homework question? There would never be a valid real life scenario for this...
– theMayer
Nov 10 at 13:45
Is this a homework question? There would never be a valid real life scenario for this...
– theMayer
Nov 10 at 13:45
|
show 1 more comment
5 Answers
5
active
oldest
votes
up vote
2
down vote
create table #a (word varchar(1)
insert into #a values('T'),('R'),('A'),('I'),('N')
declare @a varchar (10)=''
select @a=@a+ word from #a
select @a
1
The quirky update (SELECT @v=@v + ...
) is nothing to recommend to new-comers... This article explains the backgrounds... Might be the fastest, but has some dangerous traps...
– Shnugo
Nov 11 at 10:05
add a comment |
up vote
2
down vote
A simpler approach :
DECLARE
@t TABLE(word VARCHAR(50) )
INSERT INTO @t VALUES
('t'),
('r'),
('a'),
('i'),
('n')
SELECT (SELECT word FROM @t FOR XML PATH(''), TYPE).value('.', 'VARCHAR(50)')
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:10
@Shnugo I've fixed it the special character case, thanks for noting that.
– iSR5
Nov 11 at 15:18
oh no... Did you try this? What about<
and many more? See my answer for a general approach
– Shnugo
Nov 11 at 15:21
@Shnugo didn't know that trick. thanks
– iSR5
Nov 11 at 19:09
add a comment |
up vote
1
down vote
One way you can try to use CTE recursive
CREATE TABLE T(
col varchar(50)
);
insert into t values ('train');
;with cte as (
select
1 startIdx,
len(col) maxlenght,
col
from t
UNION ALL
select
startIdx+1,
maxlenght,
col
from cte
where startIdx + 1 <= maxlenght
)
select substring(col,startIdx,1),col
from cte
sqlfiddle
Result
t
r
a
i
n
If you want to let char to tranpose a column with a word, you can try to use FOR XML
create TABLE T2 (c NVARCHAR(1))
INSERT T2 VALUES('t'),('r'),('a'),('i'),('n');
SELECT STUFF((SELECT c FROM T2 FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,0,'')
sqfiddle
RESULT
train
@kkun Is that word for you?
– D-Shih
Nov 10 at 4:56
No need forSTUFF()
here... This is only needed in cases, where you add a delimiter, which must be removed to avoid it at the string's beginning.
– Shnugo
Nov 11 at 10:07
add a comment |
up vote
1
down vote
Here's a way to do the first one, especially if you need to be able to group by an account number or other columns. I also included a commented out way to do the letters as comma separated in case that's a potential need. I've used that to display all service codes on an account.
DECLARE @t TABLE (
Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Account BIGINT NOT NULL,
Value NVARCHAR(1) NOT NULL
);
INSERT INTO @t
(Account,Value)
VALUES
(1,'t'),(1,'r'),(1,'a'),(1,'i'),(1,'n'),
(2,'c'),(2,'a'),(2,'t');
SET NOCOUNT ON;
SELECT
a.Account,
REPLACE(REPLACE(v.Value,'<Value>',''),'</Value>','') AS Value
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT v.Value
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
/*
SELECT *
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT
CASE ROW_NUMBER() OVER (ORDER BY v.Id)
WHEN 1 THEN ''
ELSE ', '
END + (v.Value)
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
*/
SET NOCOUNT OFF;
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:12
You're correct on those characters. I was going based on the requirement of "transpose a column with a word". Not as dynamic as a column with any character, but there ya go. That STRING_AGG function is pretty cool though. Didn't know about that. Much cleaner.
– Utrolig
Nov 11 at 20:44
add a comment |
up vote
0
down vote
There are many answers already, most of them working for this tiny case, but all of them not unproblematic:
- The quirky update has side-effects one should really deeply know about
- All XML-approaches get in troubles with escaped characters like
& -> &
- All approaches might get in troubles with larger strings, when there is no explicit sort order and the engine decides to go in parallel mode
Here are two secure approaches to get single characters and two secure approaches to get the strings back. Try it out:
DECLARE @Word1 NVARCHAR(100)='train';
DECLARE @Word2 NVARCHAR(100)='this & that'; --the "&" can lead to troubles in XML
DECLARE @singleChars TABLE(ID INT IDENTITY,WordId INT,Chr CHAR(1));
--Tally (a set of running numbers)
--Best was an existing (physical) numbers table, but this shows the principles
WITH tally(nmbr) AS (SELECT TOP(LEN(@Word1)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) --the "FROM master..spt_values" is just to pick a table with a high number of rows... We do not need the values, just the set...
INSERT INTO @singleChars(WordId,Chr)
SELECT 1,SUBSTRING(@Word1,nmbr,1)
FROM tally;
--recursive
--This is a hidden RBAR approach (rather iterativ than recursive)
WITH cte AS
(
SELECT 1 AS ChrPos,SUBSTRING(@Word2,1,1) AS Chr
UNION ALL
SELECT cte.ChrPos +1, SUBSTRING(@Word2,cte.ChrPos+1,1)
FROM cte
WHERE cte.ChrPos+1<=LEN(@Word2)
)
INSERT INTO @singleChars(WordId,Chr)
SELECT 2,Chr
FROM cte
ORDER BY ChrPos;
--check the result
SELECT * FROM @singleChars;
--Now we re-concatenate the single characters.
--XML approach
SELECT (SELECT sc1.Chr
FROM @singleChars sc1
WHERE sc.WordId=sc1.WordId
ORDER BY ID --never forget the "ORDER BY"!
FOR XML PATH(''),TYPE).value('.','nvarchar(100)') --read with `.value()` to ensure re-escaping
FROM @singleChars sc
GROUP BY WordId;
--STRING_AGG(needs v2017+)
SELECT STRING_AGG(sc.Chr,NULL) WITHIN GROUP(ORDER BY ID) --again the "ORDER BY"!
FROM @singleChars sc
GROUP BY WordId
add a comment |
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
create table #a (word varchar(1)
insert into #a values('T'),('R'),('A'),('I'),('N')
declare @a varchar (10)=''
select @a=@a+ word from #a
select @a
1
The quirky update (SELECT @v=@v + ...
) is nothing to recommend to new-comers... This article explains the backgrounds... Might be the fastest, but has some dangerous traps...
– Shnugo
Nov 11 at 10:05
add a comment |
up vote
2
down vote
create table #a (word varchar(1)
insert into #a values('T'),('R'),('A'),('I'),('N')
declare @a varchar (10)=''
select @a=@a+ word from #a
select @a
1
The quirky update (SELECT @v=@v + ...
) is nothing to recommend to new-comers... This article explains the backgrounds... Might be the fastest, but has some dangerous traps...
– Shnugo
Nov 11 at 10:05
add a comment |
up vote
2
down vote
up vote
2
down vote
create table #a (word varchar(1)
insert into #a values('T'),('R'),('A'),('I'),('N')
declare @a varchar (10)=''
select @a=@a+ word from #a
select @a
create table #a (word varchar(1)
insert into #a values('T'),('R'),('A'),('I'),('N')
declare @a varchar (10)=''
select @a=@a+ word from #a
select @a
answered Nov 10 at 5:14
Sarvesh Bandekar
768
768
1
The quirky update (SELECT @v=@v + ...
) is nothing to recommend to new-comers... This article explains the backgrounds... Might be the fastest, but has some dangerous traps...
– Shnugo
Nov 11 at 10:05
add a comment |
1
The quirky update (SELECT @v=@v + ...
) is nothing to recommend to new-comers... This article explains the backgrounds... Might be the fastest, but has some dangerous traps...
– Shnugo
Nov 11 at 10:05
1
1
The quirky update (
SELECT @v=@v + ...
) is nothing to recommend to new-comers... This article explains the backgrounds... Might be the fastest, but has some dangerous traps...– Shnugo
Nov 11 at 10:05
The quirky update (
SELECT @v=@v + ...
) is nothing to recommend to new-comers... This article explains the backgrounds... Might be the fastest, but has some dangerous traps...– Shnugo
Nov 11 at 10:05
add a comment |
up vote
2
down vote
A simpler approach :
DECLARE
@t TABLE(word VARCHAR(50) )
INSERT INTO @t VALUES
('t'),
('r'),
('a'),
('i'),
('n')
SELECT (SELECT word FROM @t FOR XML PATH(''), TYPE).value('.', 'VARCHAR(50)')
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:10
@Shnugo I've fixed it the special character case, thanks for noting that.
– iSR5
Nov 11 at 15:18
oh no... Did you try this? What about<
and many more? See my answer for a general approach
– Shnugo
Nov 11 at 15:21
@Shnugo didn't know that trick. thanks
– iSR5
Nov 11 at 19:09
add a comment |
up vote
2
down vote
A simpler approach :
DECLARE
@t TABLE(word VARCHAR(50) )
INSERT INTO @t VALUES
('t'),
('r'),
('a'),
('i'),
('n')
SELECT (SELECT word FROM @t FOR XML PATH(''), TYPE).value('.', 'VARCHAR(50)')
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:10
@Shnugo I've fixed it the special character case, thanks for noting that.
– iSR5
Nov 11 at 15:18
oh no... Did you try this? What about<
and many more? See my answer for a general approach
– Shnugo
Nov 11 at 15:21
@Shnugo didn't know that trick. thanks
– iSR5
Nov 11 at 19:09
add a comment |
up vote
2
down vote
up vote
2
down vote
A simpler approach :
DECLARE
@t TABLE(word VARCHAR(50) )
INSERT INTO @t VALUES
('t'),
('r'),
('a'),
('i'),
('n')
SELECT (SELECT word FROM @t FOR XML PATH(''), TYPE).value('.', 'VARCHAR(50)')
A simpler approach :
DECLARE
@t TABLE(word VARCHAR(50) )
INSERT INTO @t VALUES
('t'),
('r'),
('a'),
('i'),
('n')
SELECT (SELECT word FROM @t FOR XML PATH(''), TYPE).value('.', 'VARCHAR(50)')
edited Nov 11 at 19:08
answered Nov 10 at 4:50
iSR5
1,338278
1,338278
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:10
@Shnugo I've fixed it the special character case, thanks for noting that.
– iSR5
Nov 11 at 15:18
oh no... Did you try this? What about<
and many more? See my answer for a general approach
– Shnugo
Nov 11 at 15:21
@Shnugo didn't know that trick. thanks
– iSR5
Nov 11 at 19:09
add a comment |
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:10
@Shnugo I've fixed it the special character case, thanks for noting that.
– iSR5
Nov 11 at 15:18
oh no... Did you try this? What about<
and many more? See my answer for a general approach
– Shnugo
Nov 11 at 15:21
@Shnugo didn't know that trick. thanks
– iSR5
Nov 11 at 19:09
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an
&
in the input.– Shnugo
Nov 11 at 10:10
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an
&
in the input.– Shnugo
Nov 11 at 10:10
@Shnugo I've fixed it the special character case, thanks for noting that.
– iSR5
Nov 11 at 15:18
@Shnugo I've fixed it the special character case, thanks for noting that.
– iSR5
Nov 11 at 15:18
oh no... Did you try this? What about
<
and many more? See my answer for a general approach– Shnugo
Nov 11 at 15:21
oh no... Did you try this? What about
<
and many more? See my answer for a general approach– Shnugo
Nov 11 at 15:21
@Shnugo didn't know that trick. thanks
– iSR5
Nov 11 at 19:09
@Shnugo didn't know that trick. thanks
– iSR5
Nov 11 at 19:09
add a comment |
up vote
1
down vote
One way you can try to use CTE recursive
CREATE TABLE T(
col varchar(50)
);
insert into t values ('train');
;with cte as (
select
1 startIdx,
len(col) maxlenght,
col
from t
UNION ALL
select
startIdx+1,
maxlenght,
col
from cte
where startIdx + 1 <= maxlenght
)
select substring(col,startIdx,1),col
from cte
sqlfiddle
Result
t
r
a
i
n
If you want to let char to tranpose a column with a word, you can try to use FOR XML
create TABLE T2 (c NVARCHAR(1))
INSERT T2 VALUES('t'),('r'),('a'),('i'),('n');
SELECT STUFF((SELECT c FROM T2 FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,0,'')
sqfiddle
RESULT
train
@kkun Is that word for you?
– D-Shih
Nov 10 at 4:56
No need forSTUFF()
here... This is only needed in cases, where you add a delimiter, which must be removed to avoid it at the string's beginning.
– Shnugo
Nov 11 at 10:07
add a comment |
up vote
1
down vote
One way you can try to use CTE recursive
CREATE TABLE T(
col varchar(50)
);
insert into t values ('train');
;with cte as (
select
1 startIdx,
len(col) maxlenght,
col
from t
UNION ALL
select
startIdx+1,
maxlenght,
col
from cte
where startIdx + 1 <= maxlenght
)
select substring(col,startIdx,1),col
from cte
sqlfiddle
Result
t
r
a
i
n
If you want to let char to tranpose a column with a word, you can try to use FOR XML
create TABLE T2 (c NVARCHAR(1))
INSERT T2 VALUES('t'),('r'),('a'),('i'),('n');
SELECT STUFF((SELECT c FROM T2 FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,0,'')
sqfiddle
RESULT
train
@kkun Is that word for you?
– D-Shih
Nov 10 at 4:56
No need forSTUFF()
here... This is only needed in cases, where you add a delimiter, which must be removed to avoid it at the string's beginning.
– Shnugo
Nov 11 at 10:07
add a comment |
up vote
1
down vote
up vote
1
down vote
One way you can try to use CTE recursive
CREATE TABLE T(
col varchar(50)
);
insert into t values ('train');
;with cte as (
select
1 startIdx,
len(col) maxlenght,
col
from t
UNION ALL
select
startIdx+1,
maxlenght,
col
from cte
where startIdx + 1 <= maxlenght
)
select substring(col,startIdx,1),col
from cte
sqlfiddle
Result
t
r
a
i
n
If you want to let char to tranpose a column with a word, you can try to use FOR XML
create TABLE T2 (c NVARCHAR(1))
INSERT T2 VALUES('t'),('r'),('a'),('i'),('n');
SELECT STUFF((SELECT c FROM T2 FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,0,'')
sqfiddle
RESULT
train
One way you can try to use CTE recursive
CREATE TABLE T(
col varchar(50)
);
insert into t values ('train');
;with cte as (
select
1 startIdx,
len(col) maxlenght,
col
from t
UNION ALL
select
startIdx+1,
maxlenght,
col
from cte
where startIdx + 1 <= maxlenght
)
select substring(col,startIdx,1),col
from cte
sqlfiddle
Result
t
r
a
i
n
If you want to let char to tranpose a column with a word, you can try to use FOR XML
create TABLE T2 (c NVARCHAR(1))
INSERT T2 VALUES('t'),('r'),('a'),('i'),('n');
SELECT STUFF((SELECT c FROM T2 FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,0,'')
sqfiddle
RESULT
train
edited Nov 10 at 4:35
answered Nov 10 at 4:30
D-Shih
24.4k61431
24.4k61431
@kkun Is that word for you?
– D-Shih
Nov 10 at 4:56
No need forSTUFF()
here... This is only needed in cases, where you add a delimiter, which must be removed to avoid it at the string's beginning.
– Shnugo
Nov 11 at 10:07
add a comment |
@kkun Is that word for you?
– D-Shih
Nov 10 at 4:56
No need forSTUFF()
here... This is only needed in cases, where you add a delimiter, which must be removed to avoid it at the string's beginning.
– Shnugo
Nov 11 at 10:07
@kkun Is that word for you?
– D-Shih
Nov 10 at 4:56
@kkun Is that word for you?
– D-Shih
Nov 10 at 4:56
No need for
STUFF()
here... This is only needed in cases, where you add a delimiter, which must be removed to avoid it at the string's beginning.– Shnugo
Nov 11 at 10:07
No need for
STUFF()
here... This is only needed in cases, where you add a delimiter, which must be removed to avoid it at the string's beginning.– Shnugo
Nov 11 at 10:07
add a comment |
up vote
1
down vote
Here's a way to do the first one, especially if you need to be able to group by an account number or other columns. I also included a commented out way to do the letters as comma separated in case that's a potential need. I've used that to display all service codes on an account.
DECLARE @t TABLE (
Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Account BIGINT NOT NULL,
Value NVARCHAR(1) NOT NULL
);
INSERT INTO @t
(Account,Value)
VALUES
(1,'t'),(1,'r'),(1,'a'),(1,'i'),(1,'n'),
(2,'c'),(2,'a'),(2,'t');
SET NOCOUNT ON;
SELECT
a.Account,
REPLACE(REPLACE(v.Value,'<Value>',''),'</Value>','') AS Value
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT v.Value
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
/*
SELECT *
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT
CASE ROW_NUMBER() OVER (ORDER BY v.Id)
WHEN 1 THEN ''
ELSE ', '
END + (v.Value)
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
*/
SET NOCOUNT OFF;
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:12
You're correct on those characters. I was going based on the requirement of "transpose a column with a word". Not as dynamic as a column with any character, but there ya go. That STRING_AGG function is pretty cool though. Didn't know about that. Much cleaner.
– Utrolig
Nov 11 at 20:44
add a comment |
up vote
1
down vote
Here's a way to do the first one, especially if you need to be able to group by an account number or other columns. I also included a commented out way to do the letters as comma separated in case that's a potential need. I've used that to display all service codes on an account.
DECLARE @t TABLE (
Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Account BIGINT NOT NULL,
Value NVARCHAR(1) NOT NULL
);
INSERT INTO @t
(Account,Value)
VALUES
(1,'t'),(1,'r'),(1,'a'),(1,'i'),(1,'n'),
(2,'c'),(2,'a'),(2,'t');
SET NOCOUNT ON;
SELECT
a.Account,
REPLACE(REPLACE(v.Value,'<Value>',''),'</Value>','') AS Value
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT v.Value
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
/*
SELECT *
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT
CASE ROW_NUMBER() OVER (ORDER BY v.Id)
WHEN 1 THEN ''
ELSE ', '
END + (v.Value)
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
*/
SET NOCOUNT OFF;
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:12
You're correct on those characters. I was going based on the requirement of "transpose a column with a word". Not as dynamic as a column with any character, but there ya go. That STRING_AGG function is pretty cool though. Didn't know about that. Much cleaner.
– Utrolig
Nov 11 at 20:44
add a comment |
up vote
1
down vote
up vote
1
down vote
Here's a way to do the first one, especially if you need to be able to group by an account number or other columns. I also included a commented out way to do the letters as comma separated in case that's a potential need. I've used that to display all service codes on an account.
DECLARE @t TABLE (
Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Account BIGINT NOT NULL,
Value NVARCHAR(1) NOT NULL
);
INSERT INTO @t
(Account,Value)
VALUES
(1,'t'),(1,'r'),(1,'a'),(1,'i'),(1,'n'),
(2,'c'),(2,'a'),(2,'t');
SET NOCOUNT ON;
SELECT
a.Account,
REPLACE(REPLACE(v.Value,'<Value>',''),'</Value>','') AS Value
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT v.Value
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
/*
SELECT *
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT
CASE ROW_NUMBER() OVER (ORDER BY v.Id)
WHEN 1 THEN ''
ELSE ', '
END + (v.Value)
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
*/
SET NOCOUNT OFF;
Here's a way to do the first one, especially if you need to be able to group by an account number or other columns. I also included a commented out way to do the letters as comma separated in case that's a potential need. I've used that to display all service codes on an account.
DECLARE @t TABLE (
Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Account BIGINT NOT NULL,
Value NVARCHAR(1) NOT NULL
);
INSERT INTO @t
(Account,Value)
VALUES
(1,'t'),(1,'r'),(1,'a'),(1,'i'),(1,'n'),
(2,'c'),(2,'a'),(2,'t');
SET NOCOUNT ON;
SELECT
a.Account,
REPLACE(REPLACE(v.Value,'<Value>',''),'</Value>','') AS Value
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT v.Value
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
/*
SELECT *
FROM (
SELECT DISTINCT t.Account
FROM @t AS t
) AS a
CROSS APPLY (
SELECT
CASE ROW_NUMBER() OVER (ORDER BY v.Id)
WHEN 1 THEN ''
ELSE ', '
END + (v.Value)
FROM @t AS v
WHERE v.Account = a.Account
ORDER BY v.Id
FOR XML PATH ('')
) AS v (Value);
*/
SET NOCOUNT OFF;
answered Nov 10 at 6:30
Utrolig
242211
242211
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:12
You're correct on those characters. I was going based on the requirement of "transpose a column with a word". Not as dynamic as a column with any character, but there ya go. That STRING_AGG function is pretty cool though. Didn't know about that. Much cleaner.
– Utrolig
Nov 11 at 20:44
add a comment |
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an&
in the input.
– Shnugo
Nov 11 at 10:12
You're correct on those characters. I was going based on the requirement of "transpose a column with a word". Not as dynamic as a column with any character, but there ya go. That STRING_AGG function is pretty cool though. Didn't know about that. Much cleaner.
– Utrolig
Nov 11 at 20:44
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an
&
in the input.– Shnugo
Nov 11 at 10:12
Not the best advise... At least not as a general how to approach... If there was a character in the input, which needs escaping, you'll get it back unescaped. Try this with an
&
in the input.– Shnugo
Nov 11 at 10:12
You're correct on those characters. I was going based on the requirement of "transpose a column with a word". Not as dynamic as a column with any character, but there ya go. That STRING_AGG function is pretty cool though. Didn't know about that. Much cleaner.
– Utrolig
Nov 11 at 20:44
You're correct on those characters. I was going based on the requirement of "transpose a column with a word". Not as dynamic as a column with any character, but there ya go. That STRING_AGG function is pretty cool though. Didn't know about that. Much cleaner.
– Utrolig
Nov 11 at 20:44
add a comment |
up vote
0
down vote
There are many answers already, most of them working for this tiny case, but all of them not unproblematic:
- The quirky update has side-effects one should really deeply know about
- All XML-approaches get in troubles with escaped characters like
& -> &
- All approaches might get in troubles with larger strings, when there is no explicit sort order and the engine decides to go in parallel mode
Here are two secure approaches to get single characters and two secure approaches to get the strings back. Try it out:
DECLARE @Word1 NVARCHAR(100)='train';
DECLARE @Word2 NVARCHAR(100)='this & that'; --the "&" can lead to troubles in XML
DECLARE @singleChars TABLE(ID INT IDENTITY,WordId INT,Chr CHAR(1));
--Tally (a set of running numbers)
--Best was an existing (physical) numbers table, but this shows the principles
WITH tally(nmbr) AS (SELECT TOP(LEN(@Word1)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) --the "FROM master..spt_values" is just to pick a table with a high number of rows... We do not need the values, just the set...
INSERT INTO @singleChars(WordId,Chr)
SELECT 1,SUBSTRING(@Word1,nmbr,1)
FROM tally;
--recursive
--This is a hidden RBAR approach (rather iterativ than recursive)
WITH cte AS
(
SELECT 1 AS ChrPos,SUBSTRING(@Word2,1,1) AS Chr
UNION ALL
SELECT cte.ChrPos +1, SUBSTRING(@Word2,cte.ChrPos+1,1)
FROM cte
WHERE cte.ChrPos+1<=LEN(@Word2)
)
INSERT INTO @singleChars(WordId,Chr)
SELECT 2,Chr
FROM cte
ORDER BY ChrPos;
--check the result
SELECT * FROM @singleChars;
--Now we re-concatenate the single characters.
--XML approach
SELECT (SELECT sc1.Chr
FROM @singleChars sc1
WHERE sc.WordId=sc1.WordId
ORDER BY ID --never forget the "ORDER BY"!
FOR XML PATH(''),TYPE).value('.','nvarchar(100)') --read with `.value()` to ensure re-escaping
FROM @singleChars sc
GROUP BY WordId;
--STRING_AGG(needs v2017+)
SELECT STRING_AGG(sc.Chr,NULL) WITHIN GROUP(ORDER BY ID) --again the "ORDER BY"!
FROM @singleChars sc
GROUP BY WordId
add a comment |
up vote
0
down vote
There are many answers already, most of them working for this tiny case, but all of them not unproblematic:
- The quirky update has side-effects one should really deeply know about
- All XML-approaches get in troubles with escaped characters like
& -> &
- All approaches might get in troubles with larger strings, when there is no explicit sort order and the engine decides to go in parallel mode
Here are two secure approaches to get single characters and two secure approaches to get the strings back. Try it out:
DECLARE @Word1 NVARCHAR(100)='train';
DECLARE @Word2 NVARCHAR(100)='this & that'; --the "&" can lead to troubles in XML
DECLARE @singleChars TABLE(ID INT IDENTITY,WordId INT,Chr CHAR(1));
--Tally (a set of running numbers)
--Best was an existing (physical) numbers table, but this shows the principles
WITH tally(nmbr) AS (SELECT TOP(LEN(@Word1)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) --the "FROM master..spt_values" is just to pick a table with a high number of rows... We do not need the values, just the set...
INSERT INTO @singleChars(WordId,Chr)
SELECT 1,SUBSTRING(@Word1,nmbr,1)
FROM tally;
--recursive
--This is a hidden RBAR approach (rather iterativ than recursive)
WITH cte AS
(
SELECT 1 AS ChrPos,SUBSTRING(@Word2,1,1) AS Chr
UNION ALL
SELECT cte.ChrPos +1, SUBSTRING(@Word2,cte.ChrPos+1,1)
FROM cte
WHERE cte.ChrPos+1<=LEN(@Word2)
)
INSERT INTO @singleChars(WordId,Chr)
SELECT 2,Chr
FROM cte
ORDER BY ChrPos;
--check the result
SELECT * FROM @singleChars;
--Now we re-concatenate the single characters.
--XML approach
SELECT (SELECT sc1.Chr
FROM @singleChars sc1
WHERE sc.WordId=sc1.WordId
ORDER BY ID --never forget the "ORDER BY"!
FOR XML PATH(''),TYPE).value('.','nvarchar(100)') --read with `.value()` to ensure re-escaping
FROM @singleChars sc
GROUP BY WordId;
--STRING_AGG(needs v2017+)
SELECT STRING_AGG(sc.Chr,NULL) WITHIN GROUP(ORDER BY ID) --again the "ORDER BY"!
FROM @singleChars sc
GROUP BY WordId
add a comment |
up vote
0
down vote
up vote
0
down vote
There are many answers already, most of them working for this tiny case, but all of them not unproblematic:
- The quirky update has side-effects one should really deeply know about
- All XML-approaches get in troubles with escaped characters like
& -> &
- All approaches might get in troubles with larger strings, when there is no explicit sort order and the engine decides to go in parallel mode
Here are two secure approaches to get single characters and two secure approaches to get the strings back. Try it out:
DECLARE @Word1 NVARCHAR(100)='train';
DECLARE @Word2 NVARCHAR(100)='this & that'; --the "&" can lead to troubles in XML
DECLARE @singleChars TABLE(ID INT IDENTITY,WordId INT,Chr CHAR(1));
--Tally (a set of running numbers)
--Best was an existing (physical) numbers table, but this shows the principles
WITH tally(nmbr) AS (SELECT TOP(LEN(@Word1)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) --the "FROM master..spt_values" is just to pick a table with a high number of rows... We do not need the values, just the set...
INSERT INTO @singleChars(WordId,Chr)
SELECT 1,SUBSTRING(@Word1,nmbr,1)
FROM tally;
--recursive
--This is a hidden RBAR approach (rather iterativ than recursive)
WITH cte AS
(
SELECT 1 AS ChrPos,SUBSTRING(@Word2,1,1) AS Chr
UNION ALL
SELECT cte.ChrPos +1, SUBSTRING(@Word2,cte.ChrPos+1,1)
FROM cte
WHERE cte.ChrPos+1<=LEN(@Word2)
)
INSERT INTO @singleChars(WordId,Chr)
SELECT 2,Chr
FROM cte
ORDER BY ChrPos;
--check the result
SELECT * FROM @singleChars;
--Now we re-concatenate the single characters.
--XML approach
SELECT (SELECT sc1.Chr
FROM @singleChars sc1
WHERE sc.WordId=sc1.WordId
ORDER BY ID --never forget the "ORDER BY"!
FOR XML PATH(''),TYPE).value('.','nvarchar(100)') --read with `.value()` to ensure re-escaping
FROM @singleChars sc
GROUP BY WordId;
--STRING_AGG(needs v2017+)
SELECT STRING_AGG(sc.Chr,NULL) WITHIN GROUP(ORDER BY ID) --again the "ORDER BY"!
FROM @singleChars sc
GROUP BY WordId
There are many answers already, most of them working for this tiny case, but all of them not unproblematic:
- The quirky update has side-effects one should really deeply know about
- All XML-approaches get in troubles with escaped characters like
& -> &
- All approaches might get in troubles with larger strings, when there is no explicit sort order and the engine decides to go in parallel mode
Here are two secure approaches to get single characters and two secure approaches to get the strings back. Try it out:
DECLARE @Word1 NVARCHAR(100)='train';
DECLARE @Word2 NVARCHAR(100)='this & that'; --the "&" can lead to troubles in XML
DECLARE @singleChars TABLE(ID INT IDENTITY,WordId INT,Chr CHAR(1));
--Tally (a set of running numbers)
--Best was an existing (physical) numbers table, but this shows the principles
WITH tally(nmbr) AS (SELECT TOP(LEN(@Word1)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) --the "FROM master..spt_values" is just to pick a table with a high number of rows... We do not need the values, just the set...
INSERT INTO @singleChars(WordId,Chr)
SELECT 1,SUBSTRING(@Word1,nmbr,1)
FROM tally;
--recursive
--This is a hidden RBAR approach (rather iterativ than recursive)
WITH cte AS
(
SELECT 1 AS ChrPos,SUBSTRING(@Word2,1,1) AS Chr
UNION ALL
SELECT cte.ChrPos +1, SUBSTRING(@Word2,cte.ChrPos+1,1)
FROM cte
WHERE cte.ChrPos+1<=LEN(@Word2)
)
INSERT INTO @singleChars(WordId,Chr)
SELECT 2,Chr
FROM cte
ORDER BY ChrPos;
--check the result
SELECT * FROM @singleChars;
--Now we re-concatenate the single characters.
--XML approach
SELECT (SELECT sc1.Chr
FROM @singleChars sc1
WHERE sc.WordId=sc1.WordId
ORDER BY ID --never forget the "ORDER BY"!
FOR XML PATH(''),TYPE).value('.','nvarchar(100)') --read with `.value()` to ensure re-escaping
FROM @singleChars sc
GROUP BY WordId;
--STRING_AGG(needs v2017+)
SELECT STRING_AGG(sc.Chr,NULL) WITHIN GROUP(ORDER BY ID) --again the "ORDER BY"!
FROM @singleChars sc
GROUP BY WordId
edited Nov 11 at 10:50
answered Nov 11 at 10:44
Shnugo
47.7k72566
47.7k72566
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53235852%2ftranspose-a-word-written-vertically-to-horizontally-in-sql-server%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
What RDMS are you using? I deleted my answer because it was SQL Server specific and you only tagged [sql].
– Ross Bush
Nov 10 at 4:09
I'm using Microsoft SQL Server Management Studio.
– kkun
Nov 10 at 4:14
1
Whichever answer you go with for the first part of this question, please think about the query which returns one letter on each row...The solution is dependent upon the order. So if you use it in a real scenario, make sure you have an
order by
clause in the query which returns the letters; otherwise at some point in future, SQL might decide a different access path, returning your letters in a different order.– Richardissimo
Nov 10 at 7:39
SQL Server 2017 has a function STRING_AGG() which is worth looking into if you're up to date with versions.
– e_i_pi
Nov 10 at 7:55
Is this a homework question? There would never be a valid real life scenario for this...
– theMayer
Nov 10 at 13:45