I would like to transform a pivot table into a flat table, but in the following fashion: consider the simple example of this table:
As you can see, for each item - Address or Income -, we have a column for old values, and a column for new (updated values). I would like to convert the table to a "flat" table, looking like:
Is there an easy way of doing that?
Thank you for your help!
In order to get the result, you will need to UNPIVOT the data. When you unpivot you convert the multiple columns into multiple rows, in doing so the datatypes of the data must be the same.
I would use CROSS APPLY to unpivot the columns in pairs:
select t.employee_id,
t.employee_name,
c.data,
c.old,
c.new
from yourtable t
cross apply
(
values
('Address', Address_Old, Address_new),
('Income', cast(income_old as varchar(15)), cast(income_new as varchar(15)))
) c (data, old, new);
See SQL Fiddle with demo. As you can see this uses a cast on the income columns because I am guessing it is a different datatype from the address. Since the final result will have these values in the same column the data must be of the same type.
This can also be written using CROSS APPLY with UNION ALL:
select t.employee_id,
t.employee_name,
c.data,
c.old,
c.new
from yourtable t
cross apply
(
select 'Address', Address_Old, Address_new union all
select 'Income', cast(income_old as varchar(15)), cast(income_new as varchar(15))
) c (data, old, new)
See Demo
select employee_id,employee_name,data,old,new
from (
select employee_id,employee_name,adress_old as old,adress_new as new,'ADRESS' as data
from employe
union
select employee_id,employee_name,income_old,income_new,'INCOME'
from employe
) data
order by employee_id,data
see this fiddle demo : http://sqlfiddle.com/#!2/64344/7/0
Related
I have a table where the column data has a combination of values seperated by ';'. I would like to split them into rows for each column value.
Table data
Now I would like to split them into multiple rows for each value like
I have tried using the below SQL statement.
SELECT DISTINCT COL_NAME FROM "DB"."SCHEMA"."TABLE,
LATERAL FLATTEN(INPUT=>SPLIT(COL_NAME,';'))
But the output is not as expected. Attaching the query output below.
Basically the query does nothing to my data.
It could be achieved using SPLIT_TO_TABLE table function:
This table function splits a string (based on a specified delimiter) and flattens the results into rows.
SELECT *
FROM tab, LATERAL SPLIT_TO_TABLE(column_name, ';')
I was able to resolve this by using LATERAL FLATTERN like a joining table and selecting the value from it.
SELECT DISTINCT A.VALUE AS COL_NAME
FROM "DB"."SCHEMA"."TABLE",
LATERAL SPLIT_TO_TABLE(COL_NAME,';')A
Looks your data has multiple delimiters , We can leverage STRTOK_SPLIT_TO_TABLE function using multiple delimiters..
STRTOK_SPLIT_TO_TABLE
WITH data AS (
SELECT *
FROM VALUES
('Greensboro-High Point-Winston-Salem;Norfolk-Portsmouth-Newport News Washington, D.C. Roanoke-Lynchburg Richmond-Petersburg')
v( cities))
select *
from data, lateral strtok_split_to_table(cities, ';-')
order by seq, index;
Result:
Your first attempt was very close, you just need to access the out of the flatten, instead of the input to the flatten
so using this CTE for data:
WITH fake_data AS (
SELECT *
FROM VALUES
('Greensboro-High Point-Winston-Salem;Norfolk-Portsmouth-Newport News;Washington, D.C.;Roanoke-Lynchburg;Richmond-Petersburg'),
('Knoxville'),
('Knoxville;Memphis;Nashville')
v( COL_NAME)
)
if you had aliased you tables, and accessed the parts.
SELECT DISTINCT f.value::text as col_name
FROM fake_data d,
LATERAL FLATTEN(INPUT=>SPLIT(COL_NAME,';')) f
;
which is what you did in your provided answer, but via SPLIT_TO_TABLE
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(SPLIT_TO_TABLE(COL_NAME,';')) f
;
STRTOK_SPLIT_TO_TABLE also is the same thing:
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(strtok_split_to_table(COL_NAME,';')) f
;
Which can also be done via a strtok_to_array and FLATTEN that
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(FLATTEN(input=>STRTOK_TO_ARRAY(COL_NAME,';'))) f
;
COL_NAME
Greensboro-High Point-Winston-Salem
Norfolk-Portsmouth-Newport News
Washington, D.C.
Roanoke-Lynchburg
Richmond-Petersburg
Knoxville
Memphis
Nashville
How could you convert or transpose a range of data into a single column as shown above? Values could be ambiguous in data but output should contain unique values only.
(Updated after more information was provided in comments)
If your initial data comes from a query you could use a common table expression to do this:
with query_results (a,b,c) as (
... your original query that you have not shown us goes here ...
)
select a
from query_results
union
select b
from query_results
union
select c
from query_results
order by 1
The UNION operator will remove duplicates from the output
You can use UNPIVOT:
SELECT value
FROM your_table
UNPIVOT ( value FOR type IN ( a, b, c ) );
I am trying to populate a Gridview to have checkboxes enabled per student, but depending to certain values from this query:
#SelectedDate is provided via a TextBox as a date only
SELECT v1.StudentID,
v1.StudentPreferredName + ' ' + v1.StudentFamilyName AS StudentName,
bcs.CheckStatusName,
rce.DateSubmitted,
rcp.RollCallPeriod
FROM tblBoardingRollCallEntries AS rce
INNER JOIN vwBoardingTenants AS v1
ON v1.StudentID = rce.StudentID
AND v1.[Year] = YEAR(#SelectedDate)
INNER JOIN tblBoardingCheckStatus AS bcs
ON bcs.CheckStatusID = rce.CheckStatusID
AND bcs.StatusActive = 1
INNER JOIN tblBoardingRollCallPeriods AS rcp
ON rcp.RollCallPeriodID = rce.RollCallPeriodID
AND rcp.PeriodYear = YEAR(#SelectedDate)
AND #SelectedDate BETWEEN rcp.PeriodStart AND rcp.PeriodEnd
AND rcp.RowStatus = 1
WHERE dbo.fnDateOnly(rce.DateSubmitted) = dbo.fnDateOnly(#SelectedDate)
My gridview:
Shows the following:
The data:
I want to be able to basically condense the rows in the GridView to be one student per row and the checkboxes ticked according to RollCallPeriod text.
I am playing with SQL pivots, to get the data to be as close as possible to what I am after so as to avoid code-behind, etc. However, I cannot get this to work.
select StudentID, [1],[10],[2],[3],[4],[5],[6],[7],[8],[9]
from
(
select StudentID, RollCallID, CheckStatusID
from tblBoardingRollCallEntries
unpivot
(
value for name in ([RollCallID],[StudentID],[CheckStatusID],[DateSubmitted],[StaffID])
) unpiv
) src
pivot
(
sum(RollCallPeriodID)
for RollCallPeriodID in ([1],[10],[2],[3],[4],[5],[6],[7],[8],[9])
) piv
I receive the following error:
Lookup Error - SQL Server Database Error: The type of column
"StudentID" conflicts with the type of other columns specified in the
UNPIVOT list.
Any other ideas?
Thanks
A couple of ways you can do this depending on your actual data.
This will give you the CheckStatusName as the value for the RollCallPeriod
SELECT *
FROM (
SELECT StudentName,
CheckStatusName,
RollCallPeriod
FROM [YourQueryGoesHere]
) t
PIVOT (
MAX(CheckStatusName)
FOR RollCallPeriod IN ([6:15 AM],[8:00 AM],[3:00 PM],[6:00 PM],[9:00 PM])
) p
Or you get the status and a COUNT() to show if that Student has a value for that CheckStatusName, RollCallPeriod
SELECT *
FROM (
SELECT StudentName,
CheckStatusName,
RollCallPeriod
FROM [YourQueryGoesHere]
) t
PIVOT (
COUNT(RollCallPeriod)
FOR RollCallPeriod IN ([6:15 AM],[8:00 AM],[3:00 PM],[6:00 PM],[9:00 PM])
) p
Two options:
Instead of unpivotting directly on tblBoardingRollCallEntries: first select columns cast to a VARCHAR(...) type in a derived table, then UNPIVOT the derived table. Shortened example:
select StudentID, RollCallID, CheckStatusID
from
(
SELECT ..., CAST(StudentId AS VARCHAR(128)) AS StudentId, ... FROM tblBoardingRollCallEntries)
) AS ups
unpivot
(
value for name in ([RollCallID],[StudentID],[CheckStatusID],[DateSubmitted],[StaffID])
) unpiv
Use CROSS APPLY (SELECT CAST(StudentId AS VARCHAR(128)) UNION ALL ... ) to unpivot, that way you can UNPIVOT casting the column directly to the appropriate type.
I have a table in SQL Server 2014 called anotes with the following data
and I want to add this data into another table named final as
ID Notes NoteDate
With text1, text2, text3, text4 going into the Notes column in the final table and Notedate1,notedate2,notedate3,notedate4 going into Notedate column.
I tried unpivoting the data with notes first as:
select createdid, temp
from (select createdid,text1,text2,text3,text4 from anotes) p
unpivot
(temp for note in(text1,text2,text3,text4)) as unpvt
order by createdid
Which gave me proper results:
and then for the dates part I used another unpivot query:
select createdid,temp2
from (select createdid,notedate1,notedate2,notedate3,notedate4 from anotes) p
unpivot (temp2 for notedate in(notedate1,notedate2,notedate3,notedate4)) as unpvt2
which also gives me proper results:
Now I want to add this data into my final table.
and I tried the following query and it results into a cross join :(
select a.createdid, a.temp, b.temp2
from (select createdid, temp
from (select createdid,text1,text2,text3,text4 from anotes) p
unpivot
(temp for note in(text1,text2,text3,text4)) as unpvt) a inner join (select createdid,temp2
from (select createdid,notedate1,notedate2,notedate3,notedate4 from anotes) p
unpivot (temp2 for notedate in(notedate1,notedate2,notedate3,notedate4)) as unpvt) b on a.createdid=b.createdid
The output is as follows:
Is there any way where I can unpivot both the columns at the same time?
Or use two select queries to add that data into my final table?
Thanks in advance!
I would say the most concise, and probably most efficient way to unpivot multiple columns is to use CROSS APPLY along with a table valued constructor:
SELECT t.CreatedID, upvt.Text, upvt.NoteDate
FROM anotes t
CROSS APPLY
(VALUES
(Text1, NoteDate1),
(Text2, NoteDate2),
(Text3, NoteDate3),
(Text4, NoteDate4),
(Text5, NoteDate5),
(Text6, NoteDate6),
(Text7, NoteDate7)
) upvt (Text, NoteDate);
Simplified Example on SQL Fiddle
ADDENDUM
I find the concept quite a hard one to explain, but I'll try. A table valued constuctor is simply a way of defining a table on the fly, so
SELECT *
FROM (VALUES (1, 1), (2, 2)) t (a, b);
Will Create a table with Alias t with data:
a b
------
1 1
2 2
So when you use it inside the APPLY you have access to all the outer columns, so it is just a matter of defining your constructed tables with the correct pairs of values (i.e. text1 with date1).
Used the link above mentioned by #AHiggins
Following is my final query!
select createdid,temp,temp2
from (select createdid,text1,text2,text3,text4,text5,text6,text7,notedate1,notedate2,notedate3,notedate4,notedate5,notedate6,notedate7 from anotes) main
unpivot
(temp for notes in(text1,text2,text3,text4,text5,text6,text7)) notes
unpivot (temp2 for notedate in(notedate1,notedate2,notedate3,notedate4,notedate5,notedate6,notedate7)) Dates
where RIGHT(notes,1)=RIGHT(notedate,1)
Treat each query as a table and join them together based on the createdid and the fieldid (the numeric part of the field name).
select x.createdid, x.textValue, y.dateValue
from
(
select createdid, substring(note, 5, len(note)) fieldId, textValue
from (select createdid,text1,text2,text3,text4 from anotes) p
unpivot
(textValue for note in(text1,text2,text3,text4)) as unpvt
)x
join
(
select createdid, substring(notedate, 9, len(notedate)) fieldId, dateValue
from (select createdid,notedate1,notedate2,notedate3,notedate4 from anotes) p
unpivot (dateValue for notedate in(notedate1,notedate2,notedate3,notedate4)) as unpvt2
) y on x.fieldId = y.fieldId and x.createdid = y.createdid
order by x.createdid, x.fieldId
The other answer given won't work if you have too many columns and the rightmost number of the field name is duplicated (e.g. text1 and text11).
I did some searching and didn't come up with a good answer. I am trying to create a table from data and then Unpivot it in SQL Server, but can't get the columns and rows required.
IF OBJECT_ID('tempdb..##EXPECT_PERCENTS') IS NOT NULL
DROP TABLE ##EXPECT_PERCENTS
CREATE TABLE ##EXPECT_PERCENTS (
PAYOR_ID VARCHAR(40)
,TEST_TYPE VARCHAR(40)
,[2011-08-31] INT
,[2011-09-30] INT
)
GO
INSERT INTO ##EXPECT_PERCENTS VALUES('UHC','UDT','1','2');
select * from ##EXPECT_PERCENTS
SELECT PAYOR_ID, TEST_TYPE, EXPECT
FROM ##EXPECT_PERCENTS
UNPIVOT
(
EXPECT FOR EXPECTS IN ([2011-08-31],[2011-09-30])
) AS u
I am trying to unpivot the dates as well so that there is another field called "Date" with the two dates representing each number. The first line should be:
UHC UDT 1 2011-08-31
Needing to unpivot multiple columns can often be easier without the unpivot command. One option is to use CROSS APPLY:
select payor_id, test_type, expect, expectdate
from expect_percents
cross apply
(
select [2011-08-31],'2011-08-31' union all
select [2011-09-30],'2011-09-30'
) c (expect, expectdate);
SQL Fiddle Demo
Another option is to use UNION ALL:
select payor_id, test_type, [2011-08-31] expect, '2011-08-31' date
from expect_percents
union all
select payor_id, test_type, [2011-09-30] expect, '2011-09-30' date
from expect_percents;
More Fiddle