I have a problem, I'm trying to join the same table and make column of it. For example the table contains id, name, type.
And the data can be:
id name type Date
--------------------------------------------------
1 KKKK BP 05/05/2017
2 MMMM KS 07/10/2016
3 LLL TL 04/05/2017
4 NNN BP 06/01/2016
I want to make a table with the following design:
- id name BP KS TL
-------------------------------------------------------------
1 KKK 05/05/2017
2 MMM 07/10/2016
3 LLL 04/05/2017
4 NNN 06/01/2017
I tried Pivot table and didn't work.
Any idea?
You can use the PIVOT function in SQL Server 2008 to convert your rows of data into columns:
select
id,
name,
BP, KS, TL
from
(
select id,
name,
type,
[date]
from mytable
) d
pivot
(
max([date])
for type in (BP, KS, TL)
) piv;
This could also be written using conditional logic, like a CASE expression with some aggregation:
select id,
name,
BP = max(case when type = 'BP' then [date] end),
KS = max(case when type = 'KS' then [date] end),
TL = max(case when type = 'TL' then [date] end)
from mytable
group by id, name;
Based on your comment that you could have multiple dates for each name and type combination, you can still use similar queries you'll just have to use a windowing function like row_number to get the final result you want.
If you want to use the conditional logic version, you'd change the query to be:
select
name,
BP1 = max(case when type = 'BP' and rn =1 then [date] end),
BP2 = max(case when type = 'BP' and rn =2 then [date] end),
BP3 = max(case when type = 'BP' and rn =3 then [date] end),
KS1 = max(case when type = 'KS' and rn =1 then [date] end),
KS2 = max(case when type = 'KS' and rn =2 then [date] end),
TL1 = max(case when type = 'TL' and rn =1 then [date] end)
from
(
select
name,
[type],
[date],
rn = row_number() over(partition by name, [type] order by [date] desc)
from mytable
) d
group by name;
The PIVOT version would be:
select
name,
BP1, BP2, BP3, KS1, KS2, TL1
from
(
select
name,
type = type + cast(rn as varchar(2)),
[date]
from
(
select
name,
type,
[date],
rn = row_number() over(partition by name, [type] order by [date] desc)
from mytable
)s
) d
pivot
(
max([date])
for type in (BP1, BP2, BP3, KS1, KS2, TL1)
) piv;
As you can see there is a lot of typing to get all of these columns, so you could use dynamic SQL to get the final result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(Type + cast(rn as varchar(2)))
from
(
select type,
rn = row_number() over(partition by name, type order by date desc)
from mytable
) d
group by type, rn
order by type, rn
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT name, ' + #cols + N'
from
(
select
name,
type = type + cast(rn as varchar(2)),
[date]
from
(
select
name,
type,
[date],
rn = row_number() over(partition by name, [type] order by [date] desc)
from mytable
)s
) x
pivot
(
max(date)
for type in (' + #cols + N')
) p '
exec sp_executesql #query;
I created a demo to show that they all return the same result.
Related
I have a table as
CREATE TABLE #FinalRates
(
id int primary key identity(1,1),
RateDesc nvarchar(50),
Amt decimal(18,2)
)
insert into #FinalRates values('100',200)
insert into #FinalRates values('100',300)
insert into #FinalRates values('50-80',100)
insert into #FinalRates values('50-80',300)
insert into #FinalRates values('30-50',500)
insert into #FinalRates values('30-50',250)
Looking for an output as
RateDesc Amount1 Amount2
100 200 300
50-80 100 300
30-50 500 250
I have done this as
;with cte as(
select
RateDesc
,Amounts=
STUFF((Select ','+ cast(cast(Amt as int) as varchar(10))
from #FinalRates T1
where T1.RateDesc=T2.RateDesc
FOR XML PATH('')),1,1,'')
from #FinalRates T2
group by T2.RateDesc
)
select
RateDesc,
Amount1 = PARSENAME(REPLACE(Amounts,',','.'),2),
Amount2 = PARSENAME(REPLACE(Amounts,',','.'),1)
From Cte
Drop table #FinalRates
Can the same be done using PIVOT?
That's so complicated. How about this?
select ratedesc,
max(case when seqnum = 1 then amt end) as Amount1,
max(case when seqnum = 2 then amt end) as Amount2
from (select ft.*,
row_number() over (partition by ratedesc order by id) as seqnum
from #finalrates fr
) fr
group by ratedesc;
You could use a similar approach using pivot but conditional aggregation often performs better.
Plus, if you know you have no holes in id, you can do:
select ratedesc,
max(case when id % 2 = 1 then amt end) as Amount1,
max(case when id % 2 = 0 then amt end) as Amount2
from #finalrates fr
group by ratedesc;
Using PIVOT,
Assuming you have 2 Amt for each RateDesc.
Select RateDesc, [Amount1], [Amount2] From
(
Select RateDesc, Amt
, 'Amount' + cast(row_number() over (partition by RateDesc order by Amt) as varchar(5)) RowVal
from #FinalRates
) x
PIVOT
(
MAX(Amt) For RowVal in ([Amount1], [Amount2])
) p
I have the following table:
What I want is to get to this:
EventTypeId 1 and 3 are valid start events and EventTypeId of 2 is the only valid end event.
I have tried to do a pivot, but I don't believe a pivot will get me the multiple events for a person in the result set.
SELECT PersonId, [1],[3],[2]
FROM
(
SELECT PersonId, EventTypeId, EventDate
from #PersonEvent
) as SourceTable
PIVOT
(
count(EventDate) FOR EventTypeId
IN ([1],[3],[2])
) as PivotTable
Select PersonID,
Min(Case WHEN EventTypeId IN (1,3) THEN EventDate END) as StartDate,
Min(Case WHEN EventTypeId IN (2) THEN EventDate END) as EndDate
FROM #PersonEvent
group by personid
I can do a cursor, but my original table is over 90,000 rows, and this is to be for a report, so I don't think I can use that option. Any other thoughts that I might be missing?
Assuming the table is called [dbo].[PersonEventRecords] this will work...
With StartEvents As
(
Select *
From [dbo].[PersonEventRecords]
Where EventTypeId In (1,3)
), EndEvents As
(
Select *
From [dbo].[PersonEventRecords]
Where EventTypeId In (2)
)
Select IsNull(se.PersonId,ee.PersonId) As PersonId,
se.EventTypeId As StartEventTypeId,
se.EventDate As StartEventDate,
ee.EventTypeId As EndEventTypeId,
ee.EventDate As EndEventDate
From StartEvents se
Full Outer Join EndEvents ee
On se.PersonId = ee.PersonId
And se.EventSequence = ee.EventSequence - 1
Order By IsNull(se.PersonId,ee.PersonId),
IsNull(se.EventDate,ee.EventDate);
/**** TEST DATA ****/
If Object_ID('[dbo].[PersonEventRecords]') Is Not Null
Drop Table [dbo].[PersonEventRecords];
Create Table [dbo].[PersonEventRecords]
(
PersonId Int,
EventTypeId Int,
EventDate Date,
EventSequence Int
);
Insert [dbo].[PersonEventRecords]
Select 1,1,'2012-10-13',1
Union All
Select 1,2,'2012-10-20',2
Union All
Select 1,1,'2012-11-01',3
Union All
Select 1,2,'2012-11-13',4
Union All
Select 2,1,'2012-05-07',1
Union All
Select 2,2,'2012-06-01',2
Union All
Select 2,3,'2012-07-01',3
Union All
Select 2,2,'2012-08-30',4
Union All
Select 3,2,'2012-04-05',1
Union All
Select 3,1,'2012-05-04',2
Union All
Select 3,2,'2012-05-24',3
Union All
Select 4,1,'2013-01-03',1
Union All
Select 4,1,'2013-02-20',2
Union All
Select 4,2,'2013-03-20',3;
Try this
SELECT E1.PersonId, E1.EventTypeId, E1.EventDate, E2.EventTypeId, E2.EventDate
FROM PersonEvent AS E1
OUTER APPLY(
SELECT TOP 1 PersonEvent.EventTypeId, PersonEvent.EventDate
FROM PersonEvent
WHERE PersonEvent.PersonId = E1.PersonId
AND PersonEvent.EventSequence = E1.EventSequence + 1
AND PersonEvent.EventTypeId = 2
) AS E2
WHERE E1.EventTypeId = 1 OR E1.EventTypeId = 3
UNION
SELECT E3.PersonId, NULL, NULL, E3.EventTypeId, E3.EventDate
FROM PersonEvent E3
WHERE E3.EventTypeId = 2
AND NOT EXISTS(
SELECT *
FROM PersonEvent
WHERE PersonEvent.PersonId = E3.PersonId
AND PersonEvent.EventSequence = E3.EventSequence - 1)
It is not completely clear how do you want the result to be ordered – add order as needed.
Imagine that i have a table like below,i want to write a query that give me below result,is it possible?
Result:
100 , 2015-01-01 , ABC , XYZ
You can use PIVOT.
Query
select userid,
[date],
[job],
[address]
from
(
select userid,name,[value] from tblName
)
as s
pivot
(
max([value]) for [name] in ([date], [job], [address])
) as p;
SQL Fiddle
OR
Query
select userid,
max(case when name = 'date' then [value] else null end) as [date],
max(case when name = 'job' then [value] else null end) as job,
max(case when name = 'address' then [value] else null end) as address
from tblName
group by userid;
SQL Fiddle
Another way,
try using Group_Concatenate and Common Table Expression
;with cte as
(select userid,
STUFF((
SELECT ',' + md.[value]
FROM tblName md
WHERE m.userid = md.userid
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
as result
from tblName m group by m.userid
)
select (cast(userid as varchar(50))+','+result) as res from cte
see Demo Here
How could I put those multiple rows into one line, and the contents are in different columns:
From:
ID | Subject1/Catalog/Session
10868952 | NUR/3110/D507
10868952 | NUR/3110/D512
10868952 | NUR/4010/D523
10868952 | NUR/4010/HD20
To
ID |Subject1/Catalog/Session |Subject2/Catalog/Session | Subject3/Catalog/Session |Subject4/Catalog/Session | Subject5/Catalog/Session
10868952 |NUR/3110/D507 | NUR/3110/D512 | NUR/4010/D523 | NUR/4010/HD20 |
Would be best if in the future you can provide ddl and sample data. I did this for you this time.
Here is how you could do this if you know the number of elements per row. I put links in the comments of the original post to both the static and dynamic versions of this type of approach.
if OBJECT_ID('tempdb..#Something') is not null
drop table #Something
create table #Something
(
ID int,
Subject1 varchar(50)
)
insert #Something
select 10868952, 'NUR/3110/D507' union all
select 10868952, 'NUR/3110/D512' union all
select 10868952, 'NUR/4010/D523' union all
select 10868952, 'NUR/4010/HD20';
with OrderedResults as
(
select *, ROW_NUMBER() over(partition by ID order by Subject1) as RowNum
from #Something
)
select ID
, MAX(Case when RowNum = 1 then Subject1 end) as Subject1
, MAX(Case when RowNum = 2 then Subject1 end) as Subject2
, MAX(Case when RowNum = 3 then Subject1 end) as Subject3
, MAX(Case when RowNum = 4 then Subject1 end) as Subject4
from OrderedResults
group by ID
Here is how you can do this as a dynamic pivot. There are a number of concepts going on here. One is a tally table. In this code it is implemented as a cte. In my actual system I have this as a view. It generates 10,000 rows with zero reads. The tally table and most of the other concepts here were learned by the immortal Jeff Moden. If you do not know what a tally table is or how they work, check out Jeff's article here. http://www.sqlservercentral.com/articles/T-SQL/62867/
I will post some code for how to do this for this example but anybody who is unfamiliar with this technique should read his article. http://www.sqlservercentral.com/articles/Crosstab/65048/
Here is a full working example of doing this as a dynamic cross tab. When you are satisfied that the sql this generates is safe feel free to uncomment the last two lines.
LAST but certainly not least. Make sure that you fully understand what this code and how it works. It is not going to be my phone that rings at 3am when something goes wrong. You are the one who will have to be there to support this code.
if OBJECT_ID('Something') is not null
drop table Something
create table Something
(
ID int,
Subject1 varchar(50)
)
insert Something
select 10868952, 'NUR/3110/D507' union all
select 10868952, 'NUR/3110/D512' union all
select 10868952, 'NUR/4010/D523' union all
select 10868952, 'NUR/4010/HD20' union all
select 12345, 'asdfasdf'
declare #MaxCols int
declare #StaticPortion nvarchar(2000) =
'with OrderedResults as
(
select *, ROW_NUMBER() over(partition by ID order by Subject1) as RowNum
from Something
)
select ID';
declare #DynamicPortion nvarchar(max) = '';
declare #FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by ID order by ID';
with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select #DynamicPortion = #DynamicPortion +
', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then Subject1 end) as Subject' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <=
(
select top 1 Count(*)
from Something
group by ID
order by COUNT(*) desc
)
select #StaticPortion + #DynamicPortion + #FinalStaticPortion
--declare #SqlToExecute nvarchar(max) = #StaticPortion + #DynamicPortion + #FinalStaticPortion;
--exec sp_executesql #SqlToExecute
I am talking about SQL Server 2008. I have table with three nvarchar columns. I want to select first column at Nth row, second column from Mth row, third column from Kth row where M,N,K are not equal one to another.
How to write such a query? Also is it possible to select random row?
You can use a CTE with a ROW_NUMBER() function to achieve this:
;WITH CTE AS
(
SELECT
Column1, Column2, Column3,
(your list of additional columns - if needed),
RN = ROW_NUMBER() OVER (ORDER BY InsertionDate)
)
SELECT
FirstValue = (SELECT Column1 FROM CTE WHERE RN = N),
SecondValue = (SELECT Column2 FROM CTE WHERE RN = M),
ThirdValue = (SELECT Column3 FROM CTE WHERE RN = K)
You need to replace the N, M, K with actual integer values in this query - or define SQL variables to hold those three values.
declare #i1 int;
declare #i2 int;
declare #i3 int;
SET #i1=2;
SET #i2=4;
SET #i3=1;
;with t1 as
(
select *,row_number() over(order by (select 0)) as rn from t
)
select max(CASE rn WHEN #i1 then N1 else '' end),
max(CASE rn WHEN #i2 then N2 else '' end),
max(CASE rn WHEN #i3 then N3 else '' end)
from t1 where rn in (#i1,#i2,#i3);