Select row name with max date with grouping - sql

For example:
create table #t
(
[ID] int,
[Date] date,
[Name] varchar(5)
)
insert into #t
values
(1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
select * from #t
I need to select [Name] for each [ID] with max [Data].
Something like this:
select [1], [2]
from ( select ID, [Date] from #t ) y
pivot (
max(y.[Date])
for y.ID in ([1],[2])
) pvt;
Output:
1 2
2017-04-28 2017-04-23
but instead of [Date] i want to see [Name]
what i want to view
1 2
1-1 2-2
Please help. Thank you.

Please try the following code
create table #t
(
[ID] int,
[Date] date,
[Name] varchar(5)
)
insert into #t
values
(1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
select [1], [2]
from ( select ID, [Name] from #t ) y
pivot (
max(y.[Name])
for y.ID in ([1],[2])
) pvt;
drop table #t

You can use row_nubmber() with date desc and pivot as below:
;with cte as (
select id, RowN = row_number() over (partition by id order by date desc), name from #t
) select * from
(select id, name from cte where rown = 1 ) s
pivot (max(name) for id in ([1],[2])) p

You can try the following :
SELECT [1], [2]
FROM (SELECT y.ID,
t.Name
FROM (SELECT ID,
MAX([Date]) AS [Date]
FROM #t
GROUP BY ID ) y
INNER JOIN #t t ON y.[Date] = t.[Date]
) x
PIVOT
(
MAX(x.Name)
FOR x.ID IN ([1],[2])
) pvt;
You can see this here -> http://rextester.com/ZGQGSC94965
Hope this helps!!!

try this one.
CREATE TABLE #t
(
[ID] INT ,
[Date] DATE ,
[Name] VARCHAR(5)
)
INSERT INTO #t
VALUES (1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
SELECT *
FROM #t;
WITH CTE
AS ( SELECT ID ,
MAX(Date) [Date]
FROM #t
GROUP BY ID
),
CTE2
AS ( SELECT cte.ID ,
cte.Date ,
t.name
FROM CTE
OUTER APPLY ( SELECT TOP 1
name
FROM #t
WHERE (ID = cte.ID AND Date = cte.Date)
) T
)
SELECT MAX([1]) [1] ,
MAX([2]) [2]
FROM ( SELECT ID ,
[Date] ,
NAME
FROM CTE2
) y PIVOT ( MAX(y.NAME) FOR y.ID IN ( [1], [2] ) ) pvt
Result
ID Date Name
----------- ---------- -----
1 2017-05-02 1-1
2 2017-04-22 2-1
2 2017-04-27 2-2
1 2017-04-02 1-2
(4 row(s) affected)
1 2
----- -----
1-1 2-2
(1 row(s) affected)

Related

Calculate date difference between dates in different rows of a dataset

A table looks like this :
CREATE TABLE [dbo].[HistDT](
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL,
) ON [PRIMARY]
Example data set:
ID | StartDtSK | StartDt | Status |
1 20190520 20-05-2019 12:00:13 10
1 20190520 20-05-2019 10:00:00 5
1 20190414 14-04-2019 13:23:00 2
2 20190312 12-03-2019 10:03:00 10
2 20190308 08-03-2019 18:03:00 1
etc..
I need a query which will display the number of days spent in each status. That would be easy if the table i inherited had an end date. I would then calculate the datediff and pivot for column status values.
Maybe i should create a new table using ssis where i will add an EndDt column which will be the StartDt of the latest added Status.
But is there any way to do this without creating another table?
SQL Server 2008
This is not very pretty, and I haven't tested it for all use cases. I hope you can use it or find inspiration. I'm sure there is a better way :)
declare #table2 table (
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL
)
insert into #table2
values
(1 , '20190520','2019-05-20 12:00:13','10'),
(1 , '20190520','2019-05-20 10:00:00','5'),
(1 , '20190414','2019-04-14 13:23:00','2'),
(2, '20190312', '2019-03-12 10:03:00', '10'),
(2 , '20190308', '2019-03-08 18:03:00', '1')
select *,DATEDIFF(dd,startdt,enddate) as TotalDAys from (
select x.ID,StartDtSK,Startdt,[Status],Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt) as rn from #table2
) x
cross apply ( select * from (select id,StartDt as Enddate,ROW_NUMBER() over(partition by id order by startdt) as rn2 from #table2 b
)f where (rn +1 = f.rn2 ) and x.id = f.id ) d
union all
select ID,StartDtSK,startdt,[Status],'9999-12-31' as Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt desc) as rn from #table2
)X where rn=1
)y
order by id,startdt
SQL Server 2008 without cross apply
This might be a bit more pretty :)
select *,DATEDIFF(dd,startdt,enddate) as TotalDAys from (
select x.ID,StartDtSK,Startdt,[Status],case when Enddate is null then '9999-12-31' else Enddate end as Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt) as rn from #table2
) x
left join (
select * from (select id,StartDt as Enddate,ROW_NUMBER() over(partition by id order by startdt) as rn2 from #table2 b
)f ) d on (rn +1 = d.rn2 ) and x.id = d.id
)y
SQL Server 2012 and above:
Is this what you want?
declare #table2 table (
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL
)
insert into #table2
values
(1 , '20190520','2019-05-20 12:00:13','10'),
(1 , '20190520','2019-05-20 10:00:00','5'),
(1 , '20190414','2019-04-14 13:23:00','2')
select *,Datediff(dd,Startdt,Enddate) as TotalDays from (
select *,LAG(StartDt,1,'9999-12-31') over(partition by ID order by StartDT desc) as EndDate from #table2
)x
Insert a rule that handles current status (9999-12-31) date
Maybe LEAD function is usefull for your question.
Like this
IsNull(DateAdd(SECOND,-1,Cast(LEAD ([StartDt],1) OVER (PARTITION BY [status] ORDER BY [StartDt]) AS DATETIME)),getdate()) AS EndDate

UNPIVIOT AND RANK the data

I have a table with 24 hrs data, i want to pivot the data and assign a rank for them based on the counts.
create table #HourlyData_Counts
(Hr_1 int,Hr_2 int ,Hr_3 int,Hr_4 int,Hr_5 int ,Hr_6 int ,Hr_7 int , Hr_8 int ,Hr_9 int , Hr_10 int )
insert into #HourlyData_Counts
values (55,89,78,77,67,99,45,33,23,91)
select * from #HourlyData_Counts
--drop table #HourlyData_Counts
required output
You will need to use Unpivot in this case.
select Hours,counts,Ranks=Row_Number()over(order by counts)
from
(
select * from #HourlyData_Counts
) src
UNPIVOT
(
counts For Hours in(Hr_1,Hr_2,Hr_3,Hr_4,Hr_5,Hr_6,Hr_7,Hr_8,Hr_9,Hr_10)
) unpiv;
You are looking for unpivot, like this:
select
[Hours],
[counts],
rank() over (order by [counts] desc)
from
(
select * from #HourlyData_Counts
) as src
unpivot
(
[counts] for [Hours] in (Hr_1, Hr_2, Hr_3, Hr_4, Hr_5, Hr_6, Hr_7, Hr_8, Hr_9, Hr_10)
) as upvt
order by try_cast(replace([Hours], 'Hr_', '') as int)
CREATE TABLE #HourlyData_Counts
(Hr_1 int,Hr_2 int ,Hr_3 int,Hr_4 int,Hr_5 int ,Hr_6 int ,Hr_7 int , Hr_8 int ,Hr_9 int , Hr_10 int )
INSERT INTO #HourlyData_Counts
VALUES (55,89,78,77,67,99,45,33,23,91)
SELECT
[Hours]
, Counts
, RANK() OVER (ORDER BY Counts DESC) Ranks
FROM
(
SELECT Hr_1, Hr_2, Hr_3, Hr_4, Hr_5, Hr_6, Hr_7, Hr_8, Hr_9, Hr_10
FROM #HourlyData_Counts
) P
UNPIVOT
(
Counts FOR [Hours] IN
(Hr_1, Hr_2, Hr_3, Hr_4, Hr_5, Hr_6, Hr_7, Hr_8, Hr_9, Hr_10)
) U
ORDER BY CAST(REPLACE([Hours], 'Hr_', '') AS int)
DROP TABLE #HourlyData_Counts
IF OBJECT_ID('tempdb..#HourlyData_Counts') IS NOT NULL
DROP TABLE #HourlyData_Counts
create table #HourlyData_Counts
(Hr_1 int,Hr_2 int ,Hr_3 int,Hr_4 int,Hr_5 int ,Hr_6 int ,Hr_7 int , Hr_8 int ,Hr_9 int , Hr_10 int )
insert into #HourlyData_Counts
values (55,89,78,77,67,99,45,33,23,91)
select * from #HourlyData_Counts
SELECT hours,hour1, RANK() OVER (order by hour1 desc) as RANK
FROM
(
SELECT Hr_1, Hr_2, Hr_3, Hr_4, Hr_5, Hr_6, Hr_7, Hr_8, Hr_9, Hr_10
FROM #HourlyData_Counts
) as s
UNPIVOT
(
hour1
FOR hours IN
(
Hr_1,Hr_2,Hr_3,Hr_4,Hr_5,Hr_6,Hr_7,Hr_8,Hr_9,Hr_10
)
)AS pvt
order by hours
You can use the UNPIVOT for this, so your query shoud look like this
SELECT hr,counts, RANK() OVER(ORDER BY counts DESC) FROM
(SELECT * FROM #HourlyData_Counts) p
UNPIVOT
(counts FOR hr IN (Hr_1 ,Hr_2 ,Hr_3 ,Hr_4 ,Hr_5 ,Hr_6 ,Hr_7 , Hr_8 ,Hr_9 , Hr_10 ) )AS unpvt;
RESULT
Hr_6 99 1
Hr_10 91 2
Hr_2 89 3
Hr_3 78 4
Hr_4 77 5
Hr_5 67 6
Hr_1 55 7
Hr_7 45 8
Hr_8 33 9
Hr_9 23 10
If the number of columns and names of the columns are fixed, then you can use
;with r(hr, data_count) as (
select 'hr_1', hr_1 from #HourlyData_Counts union all
select 'hr_2', hr_2 from #HourlyData_Counts union all
select 'hr_3', hr_3 from #HourlyData_Counts union all
select 'hr_4', hr_4 from #HourlyData_Counts union all
select 'hr_5', hr_5 from #HourlyData_Counts union all
select 'hr_6', hr_6 from #HourlyData_Counts union all
select 'hr_7', hr_7 from #HourlyData_Counts union all
select 'hr_8', hr_8 from #HourlyData_Counts union all
select 'hr_9', hr_9 from #HourlyData_Counts union all
select 'hr_10', hr_10 from #HourlyData_Counts
)
select hr, data_count, data_rank = ROW_NUMBER() over(order by data_count asc)
from r
order by data_count

How to replace the 'Strings' with numerical values based on a group by clause

I have the below table (#temp1) where I need to replace the string in the column'Formula' with the matching input 'VALUE' column based on the group 'Yearmonth'.
The 'Formula' column may be of any mathematical expression for better understanding I have mentioned a simple example below.
IDNUM formula INPUTNAME VALUE YEARMONTH
---------------------------------------------------------------------
1 imports(398)+imports(399) imports(398) 17.000 2003:1
2 imports(398)+imports(399) imports(398) 56.000 2003:2
3 imports(398)+imports(399) imports(399) 15.000 2003:1
4 imports(398)+imports(399) imports(399) 126.000 2003:2
For eg :From the above table i need the output as
Idnum Formula Yearmonth
1. 17.00 +15.00 2003:1
2. 56.00 +126.00 2003:2
I tried with the below different query from various suggestions but coludnt achieve it. Could someone help me this out ?
Type1 :
SELECT
REPLACE(FORMULA, INPUTName, AttributeValue) AS realvalues,
yearmonth
FROM #temp1
GROUP BY yearmonth
TYPE2 :
USING XML PATH... In this case it got worked but I need to replace only the strings with the values and not to stuff the strings based on mathematcal operation.(Because the formula might be of any type).
SELECT
IDNUM = MIN(IDNUM),
FORMULA =
(SELECT STUFF(
(SELECT ' +' + CONVERT(VARCHAR(10), Value)
FROM #temp1
WHERE YEARMONTH = t1.YEARMONTH
FOR XML PATH(''))
,1, 2, '')),
YEARMONTH
FROM #TEMP1 t1
GROUP BY YEARMONTH
TYPE3:Using Recursions...This is returning only the null values...
;with t as (
select t.*,
row_number() over (partition by yearmonth order by idnum) as seqnum,
count(*) over (partition by yearmonth) as cnt
from #temp1 t
)
,cte as (
select t.seqnum, t.yearmonth, t.cnt,
replace(formula, inputname, AttributeValue) as formula1
from t
where seqnum = 1
union all
select cte.seqnum, cte.yearmonth, cte.cnt,
replace(CTE.formula1, T.inputname, T.AttributeValue) as formula2
from cte join
t
on cte.yearmonth = t.yearmonth
AND cte.seqnum = t.seqnum + 1
)
select row_number() over (order by (select null)) as id,formula1
from cte
where seqnum = cnt
This is full working example using recursive CTE:
DECLARE #DataSource TABLE
(
[IDNUM] TINYINT
,[formula] VARCHAR(MAX)
,[INPUTNAME] VARCHAR(128)
,[VALUE] DECIMAL(9,3)
,[YEARMONTH] VARCHAR(8)
);
INSERT INTO #DataSource ([IDNUM], [formula], [INPUTNAME], [VALUE], [YEARMONTH])
VALUES ('1', 'imports(398)+imports(399)', 'imports(398)', '17.000', '2003:1')
,('2', 'imports(398)+imports(399)', 'imports(398)', '56.000', '2003:2')
,('3', 'imports(398)+imports(399)', 'imports(399)', '15.000', '2003:1')
,('4', 'imports(398)+imports(399)', 'imports(399)', '126.000', '2003:2')
,('5', '(imports(391)+imports(392)-imports(393))/imports(394)', 'imports(391)', '5.000', '2003:3')
,('6', '(imports(391)+imports(392)-imports(393))/imports(394)', 'imports(392)', '10.000', '2003:3')
,('7', '(imports(391)+imports(392)-imports(393))/imports(394)', 'imports(393)', '3.000', '2003:3')
,('8', '(imports(391)+imports(392)-imports(393))/imports(394)', 'imports(394)', '-5.000', '2003:3');
WITH DataSource AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY [YEARMONTH] ORDER BY [IDNUM]) AS [ReplacementOrderID]
,[YEARMONTH]
,[formula]
,[INPUTNAME] AS [ReplacementString]
,[VALUE] AS [ReplacementValue]
FROM #DataSource
),
RecursiveDataSource AS
(
SELECT [ReplacementOrderID]
,[YEARMONTH]
,REPLACE([formula], [ReplacementString], [ReplacementValue]) AS [formula]
FROM DataSource
WHERE [ReplacementOrderID] = 1
UNION ALL
SELECT DS.[ReplacementOrderID]
,DS.[YEARMONTH]
,REPLACE(RDS.[formula], DS.[ReplacementString], DS.[ReplacementValue]) AS [formula]
FROM RecursiveDataSource RDS
INNER JOIN DataSource DS
ON RDS.[ReplacementOrderID] + 1 = DS.[ReplacementOrderID]
AND RDS.[YEARMONTH] = DS.[YEARMONTH]
)
SELECT RDS.[YEARMONTH]
,RDS.[formula]
FROM RecursiveDataSource RDS
INNER JOIN
(
SELECT [YEARMONTH]
,MAX([ReplacementOrderID]) AS [ReplacementOrderID]
FROM DataSource
GROUP BY [YEARMONTH]
) DS
ON RDS.[YEARMONTH] = DS.[YEARMONTH]
AND RDS.[ReplacementOrderID] = DS.[ReplacementOrderID]
ORDER BY RDS.[YEARMONTH]
Generally, you simply want to perform multiple replacements over a string in one statement. You can have many replacement values, just play with the MAXRECURSION option.
--Create sample data
DROP TABLE #temp1
CREATE TABLE #temp1 (IDNUM int, formula varchar(max), INPUTNAME varchar(max), VALUE decimal, YEARMONTH varchar(max))
INSERT INTO #temp1 VALUES
(1, 'imports(398)+imports(399)', 'imports(398)', 17.000, '2003:1'),
(2, 'imports(398)+imports(399)', 'imports(398)', 56.000, '2003:2'),
(3, 'imports(398)+imports(399)', 'imports(399)', 15.000, '2003:1'),
(4, 'imports(398)+imports(399)', 'imports(399)', 126.000, '2003:2')
--Query
;WITH t as (
SELECT formula, YEARMONTH, IDNUM
FROM #temp1
UNION ALL
SELECT REPLACE(a.formula, b.INPUTNAME, CAST(b.VALUE AS varchar(100))) AS formula, a.YEARMONTH, a.IDNUM
FROM t a
JOIN #temp1 b ON a.YEARMONTH = b.YEARMONTH AND a.formula LIKE '%' + b.INPUTNAME + '%'
)
SELECT MIN(IDNUM) AS IDNUM, formula, YEARMONTH
FROM t
WHERE formula not LIKE '%imports(%'
GROUP BY formula, YEARMONTH

How to pivot column values of the below table?

TableA:
Brand Product
------------------
A X
A XX
A XXX
B Y
B YY
C Z
I need data as shown in Table below:
A B C
-------------------
X Y Z
XX YY NULL
XXX NULL NULL
How to do that in Sql Server 2008 ?
I dont beleive a PIVOT is what you are looking for here.
From what I can see you are looking at using the entries in order to generate the rows?
Also, PIVOTs make use of aggregate functions, so I cant see this happening.
What you can try, is something like
DECLARE #Table TABLE(
Brand VARCHAR(10),
Product VARCHAR(10)
)
INSERT INTO #Table SELECT 'A','X '
INSERT INTO #Table SELECT 'A','XX'
INSERT INTO #Table SELECT 'A','XXX'
INSERT INTO #Table SELECT 'B','Y'
INSERT INTO #Table SELECT 'B','YY'
INSERT INTO #Table SELECT 'C','Z'
;WITH Vals AS (
SELECT *,
ROW_NUMBER() OVER(PARTITION BY Brand ORDER BY (SELECT NULL)) RID
FROM #Table
)
, RIDs AS (
SELECT DISTINCT
RID
FROM Vals
)
SELECT vA.Product [A],
vB.Product [B],
vC.Product [C]
FROM RIDs r LEFT JOIN
Vals vA ON r.RID = vA.RID
AND vA.Brand = 'A' LEFT JOIN
Vals vB ON r.RID = vB.RID
AND vB.Brand = 'B' LEFT JOIN
Vals vC ON r.RID = vC.RID
AND vC.Brand = 'C'
I know it is a late entry, but here is a different approach to solve it:
DECLARE #Table TABLE(Brand VARCHAR(10), Product VARCHAR(10))
INSERT INTO #Table SELECT 'A','X '
INSERT INTO #Table SELECT 'A','XX'
INSERT INTO #Table SELECT 'A','XXX'
INSERT INTO #Table SELECT 'B','Y'
INSERT INTO #Table SELECT 'B','YY'
INSERT INTO #Table SELECT 'C','Z'
SELECT [A],[B],[C] FROM (
SELECT row_number() over (partition by brand order by product) rn,
Product, brand FROM #table
) as p
PIVOT(
MAX(product) for Brand in ([A],[B],[C])
)as pvt

SQL: Remove duplicates

How do I remove duplicates from a table that is set up in the following way?
unique_ID | worker_ID | date | type_ID
A worker can have multiple type_ID's associated with them and I want to remove any duplicate types. If there is a duplicate, I want to remove the type with the most recent entry.
A textbook candidate for the window function row_number():
;WITH x AS (
SELECT unique_ID
,row_number() OVER (PARTITION BY worker_ID,type_ID ORDER BY date) AS rn
FROM tbl
)
DELETE FROM tbl
FROM x
WHERE tbl.unique_ID = x.unique_ID
AND x.rn > 1
This also takes care of the situation where a set of dupes on (worker_ID,type_ID) shares the same date.
See the simplified demo on data.SE.
Update with simpler version
Turns out, this can be simplified: In SQL Server you can delete from the CTE directly:
;WITH x AS (
SELECT unique_ID
,row_number() OVER (PARTITION BY worker_ID,type_ID ORDER BY date) AS rn
FROM tbl
)
DELETE x
WHERE rn > 1
delete from table t
where exists ( select 1 from table t2
where t2.worker_id = t.worker_id
and t2.type_id = t.type_id
and t2.date < t.date )
HTH
DELETE FROM #t WHERE unique_Id IN
(
SELECT unique_Id FROM
(
SELECT unique_Id
,Type_Id
,ROW_NUMBER() OVER (PARTITION BY worker_Id, type_Id ORDER BY date) AS rn
FROM #t
) Q
WHERE rn > 1
)
And to test...
DECLARE #t TABLE
(
unique_ID INT IDENTITY,
worker_ID INT,
date DATETIME,
type_ID INT
)
INSERT INTO #t VALUES (1, DATEADD(DAY, 1, GETDATE()), 1)
INSERT INTO #t VALUES (1, GETDATE(), 1)
INSERT INTO #t VALUES (2, GETDATE(), 1)
INSERT INTO #t VALUES (1, DATEADD(DAY, 2, GETDATE()), 1)
INSERT INTO #t VALUES (1, DATEADD(DAY, 3, GETDATE()), 2)
SELECT * FROM #t
DELETE FROM #t WHERE unique_Id IN
(
SELECT unique_Id FROM
(
SELECT unique_Id
,Type_Id
,ROW_NUMBER() OVER (PARTITION BY worker_Id, type_Id ORDER BY date) AS rn
FROM #t
) Q
WHERE rn > 1
)
SELECT * FROM #t
you may use this query
delete from worker where unique_id in (
select max(unique_id) from worker group by worker_ID , type_ID having count(type_id)>1)
here i am assuming worker as your table name