How do I simply switch columns with rows in SQL? Is there any simple command to transpose?
ie turn this result:
Paul | John | Tim | Eric
Red 1 5 1 3
Green 8 4 3 5
Blue 2 2 9 1
into this:
Red | Green | Blue
Paul 1 8 2
John 5 4 2
Tim 1 3 9
Eric 3 5 1
PIVOT
seems too complex for this scenario.
9条答案
按热度按时间af7jpaap1#
There are several ways that you can transform this data. In your original post, you stated that
PIVOT
seems too complex for this scenario, but it can be applied very easily using both theUNPIVOT
andPIVOT
functions in SQL Server.However, if you do not have access to those functions this can be replicated using
UNION ALL
toUNPIVOT
and then an aggregate function with aCASE
statement toPIVOT
:Create Table:
Union All, Aggregate and CASE Version:
See SQL Fiddle with Demo
The
UNION ALL
performs theUNPIVOT
of the data by transforming the columnsPaul, John, Tim, Eric
into separate rows. Then you apply the aggregate functionsum()
with thecase
statement to get the new columns for eachcolor
.Unpivot and Pivot Static Version:
Both the
UNPIVOT
andPIVOT
functions in SQL server make this transformation much easier. If you know all of the values that you want to transform, you can hard-code them into a static version to get the result:See SQL Fiddle with Demo
The inner query with the
UNPIVOT
performs the same function as theUNION ALL
. It takes the list of columns and turns it into rows, thePIVOT
then performs the final transformation into columns.Dynamic Pivot Version:
If you have an unknown number of columns (
Paul, John, Tim, Eric
in your example) and then an unknown number of colors to transform you can use dynamic sql to generate the list toUNPIVOT
and thenPIVOT
:See SQL Fiddle with Demo
The dynamic version queries both
yourtable
and then thesys.columns
table to generate the list of items toUNPIVOT
andPIVOT
. This is then added to a query string to be executed. The plus of the dynamic version is if you have a changing list ofcolors
and/ornames
this will generate the list at run-time.All three queries will produce the same result:
kxeu7u2r2#
This normally requires you to know ALL the column AND row labels beforehand. As you can see in the query below, the labels are all listed in their entirely in both the UNPIVOT and the (re)PIVOT operations.
MS SQL Server 2012 Schema Setup:
Query 1:
Results:
Additional Notes:
wfypjpf43#
I'd like to point out few more solutions to transposing columns and rows in SQL.
The first one is - using CURSOR. Although the general consensus in the professional community is to stay away from SQL Server Cursors, there are still instances whereby the use of cursors is recommended. Anyway, Cursors present us with another option to transpose rows into columns.
Similar to the PIVOT, the cursor has the dynamic capability to append more rows as your dataset expands to include more policy numbers.
Unlike the PIVOT, the cursor excels in this area as it is able to expand to include newly added document, without altering the script.
The major limitation of transposing rows into columns using CURSOR is a disadvantage that is linked to using cursors in general – they come at significant performance cost. This is because the Cursor generates a separate query for each FETCH NEXT operation.
Another solution of transposing rows into columns is by using XML.
The XML solution to transposing rows into columns is basically an optimal version of the PIVOT in that it addresses the dynamic column limitation.
The XML version of the script addresses this limitation by using a combination of XML Path, dynamic T-SQL and some built-in functions (i.e. STUFF, QUOTENAME).
Similar to the PIVOT and the Cursor, newly added policies are able to be retrieved in the XML version of the script without altering the original script.
Unlike the PIVOT, newly added documents can be displayed without altering the script.
In terms of IO, the statistics of the XML version of the script is almost similar to the PIVOT – the only difference is that the XML has a second scan of dtTranspose table but this time from a logical read – data cache.
You can find some more about these solutions (including some actual T-SQL exmaples) in this article: https://www.sqlshack.com/multiple-options-to-transposing-rows-into-columns/
sczxawaw4#
Based on this solution from bluefeet here is a stored procedure that uses dynamic sql to generate the transposed table. It requires that all the fields are numeric except for the transposed column (the column that will be the header in the resulting table):
You can test it with the table provided with this command:
yebdmbv45#
Adding to @Paco Zarate's terrific answer above, if you want to transpose a table which has multiple types of columns, then add this to the end of line 39, so it only transposes
int
columns:Here is the full query that is being changed:
To find other
system_type_id
's, run this:omqzjyyz6#
This way Convert all Data From Filelds(Columns) In Table To Record (Row).
zbwhf8kr7#
I'm doing
UnPivot
first and storing the results inCTE
and using theCTE
inPivot
operation.Demo
jchrr9hc8#
I like to share the code I'm using to transpose a splitted text based on +bluefeet answer. In this approach I'm implemented as a procedure in MS SQL 2005
I'm mixing this solution with the information about howto order rows without order by ( SQLAuthority.com ) and the split function on MSDN ( social.msdn.microsoft.com )
When you execute the procedure
you obtain the next result
neskvpey9#
I was able to use Paco Zarate's solution and it works beautifully. I did have to add one line ("SET ANSI_WARNINGS ON"), but that may be something unique to the way I used it or called it. There is a problem with my usage and I hope someone can help me with it:
The solution works only with an actual SQL table. I tried it with a temporary table and also an in-memory (declared) table but it doesn't work with those. So in my calling code I create a table on my SQL database and then call SQLTranspose. Again, it works great. It's just what I want. Here's my problem:
In order for the overall solution to be truly dynamic I need to create that table where I temporarily store the prepared information that I'm sending to SQLTranspose "on the fly", and then delete that table once SQLTranspose is called. The table deletion is presenting a problem with my ultimate implementation plan. The code needs to run from an end-user application (a button on a Microsoft Access form/menu). When I use this SQL process (create a SQL table, call SQLTranspose, delete SQL table) the end user application hits an error because the SQL account used does not have the rights to drop a table.
So I figure there are a few possible solutions:
I recognize that some of this may warrant another, separate thread and question. However, since there is a possibility that one solution may be simply a different way to do the transposing of rows and columns I'll make my first post here in this thread.
EDIT: I also did replace sum(value) with max(value) in the 6th line from the end, as Paco suggested.
EDIT:
I figured out something that works for me. I don't know if it's the best answer or not.
I have a read-only user account that is used to execute strored procedures and therefore generate reporting output from a database. Since the SQLTranspose function I created will only work with a "legitimate" table (not a declared table and not a temporary table) I had to figure out a way for a read-only user account to create (and then later delete) a table.
I reasoned that for my purposes it's okay for the user account to be allowed to create a table. The user still could not delete the table though. My solution was to create a schema where the user account is authorized. Then whenever I create, use, or delete that table refer it with the schema specified.
I first issued this command from a 'sa' or 'sysadmin' account: CREATE SCHEMA ro AUTHORIZATION
When any time I refer to my "tmpoutput" table I specify it like this example:
drop table ro.tmpoutput