Get next row if coloumn has same value - sql

Good day, I'm trying to select a data. Please check my query and table first.
I have this query
select * from
(
SELECT * from
(
select distinct TOP 201233 row, b.OutletCode as 'Kode Otlet',
i.Description as 'Area',
c.Nilai,a.Nip, b.Fullname 'Nama Lengkap', f.positioncode as 'Posisi Sebelumnya',
case when a.[status]=0
then j.ApprovedDate else p.ApprovedDate
end as 'Tanggal Upgrade/Demosi Sebelumnya',
d.positioncode as 'Posisi Baru', a.tanggal as 'Tanggal Upgrade/Demosi'
from penilaian_header a
left join Employee b on a.Nip = b.Nip
left join Position f on b.PositionCode = f.PositionCode
left join Position d on a.PositionCode = d.PositionCode
left join arealeader g on g.OutletCode = b.OutletCode
left join outlet h on g.OutletCode = h.OutletCode
left join area i on i.areacode = h.areacode
left join cutoff k on a.periode = k.cutoffcode
left join
(select ROW_NUMBER() OVER(PARTITION BY KodePenilaianH ORDER BY idPenilaiand ASC) AS Row,
Nilai,KodePenilaianH from penilaian_Detail ) c on a.KodePenilaian = c.KodePenilaianH
left join
( SELECT ROW_NUMBER() OVER (PARTITION BY nip ORDER BY ApprovedDate desc) AS rownumber,
ApprovedDate, Nip FROM historyposition ) AS p on a.nip=p.nip and p.rownumber = 2
left join ( SELECT ROW_NUMBER() OVER (PARTITION BY nip ORDER BY ApprovedDate desc) AS rownumber, ApprovedDate, Nip
FROM historyposition ) AS j on a.nip=j.nip and j.rownumber = 1
where a.flag = 1 and h.AreaCode like '%%'
and Periode like '%CO-2016-9-16-15%' and a.nip = '1004863'
--and tanggal <= k.[to] and tanggal >= k.[from]
order by i.Description asc) nilai pivot ( sum(nilai) for row in ([1],[2],[3],[4],[5]) ) piv)A order by Area;
with above query i get this result
and i have this table HistoryPosition
KodeMutasiP OldPosition NewPosition ApprovedBy ApprovedDate Nip KodePenilaian
HP0000514 P007 P007 0802678 2016-09-15 1004863 PE0000787
HP0000513 P007 P007 0802678 2016-04-04 1004863 PE0000130
NULL NULL P007 NULL 2016-04-04 1004863 NULL
NULL NULL P041 NULL 2016-01-20 1004863 NULL
NULL NULL P007 NULL 2015-02-12 1004863 NULL
ok. So what i want to do is set Posisi Sebelumnya and Tanggal Upgrade/Demosi Sebelumnya in the first picture with newposition and Approveddate from the second picture.
But with this condition
if newposition and nip col in the second picture same with the second row then jump to the second, if the second same with the third then jump to the fourth, if the fourth different with the third then choose the third.
I have write the query to choose it here is my query
select * INTO #tMP from historyposition where nip = '1004863' order by approveddate desc
declare #NewPosition varchar(50), #NewPositionLast varchar(50), #ApproveDate datetime, #ApproveDateLast datetime
select top 1 #NewPositionLast = NewPosition, #ApproveDateLast=ApprovedDate from #tmp
WHILE EXISTS (SELECT * FROM #tMP)
BEGIN
select top 1 #NewPosition = NewPosition, #ApproveDate=ApprovedDate from #tmp
if (#NewPosition = #NewPositionLast)
begin
set #NewPositionLast = #NewPosition
set #ApproveDateLast = #ApproveDate
end
else
begin
break
end
delete top(1) #tMP
END
select #NewPositionLast , #ApproveDateLast
drop table #tMP
and i get this result
| P007 | 2016-04-04 00:00:00.000 |
the result is like what i expected.
so my question how to set the Posisi Sebelumnya and Tanggal Upgrade/Demosi Sebelumnya with the result of my last query.
Sorry for my bad english.
here it's my expected result .
I know it's same with the the first picture. But its not right. that because in my first query i choose the second row.
select * from
(
SELECT * from
(
select distinct TOP 201233 row, b.OutletCode as 'Kode Otlet', i.Description as 'Area',
c.Nilai,a.Nip, b.Fullname 'Nama Lengkap',
case when
l.ShortDesc IS NULL then f.ShortDesc else l.ShortDesc
end
as 'PosisiSebelumnya',
-----
z.approveddate,
------
d.ShortDesc as 'Posisi Baru', a.tanggal as 'Tanggal Upgrade/Demosi'
from penilaian_header a
left join Employee b on a.Nip = b.Nip
left join Position f on b.PositionCode = f.PositionCode
left join Position d on a.PositionCode = d.PositionCode
left join position l on a.posisisaatini = l.positioncode
left join arealeader g on g.OutletCode = b.OutletCode
left join outlet h on g.OutletCode = h.OutletCode
left join area i on i.areacode = h.areacode
left join cutoff k on a.periode = k.cutoffcode
left join
(select ROW_NUMBER() OVER(PARTITION BY KodePenilaianH ORDER BY idPenilaiand ASC) AS Row,
Nilai,KodePenilaianH from penilaian_Detail ) c on a.KodePenilaian = c.KodePenilaianH
left join (
select top(1) Nip,NewPosition, ApprovedDate
from (
select Nip,NewPosition,ApprovedDate
, grp = row_number() over(order by ApprovedDate desc) - row_number() over(partition by NewPosition order by ApprovedDate desc)
from HistoryPosition
) t
where grp = 0
order by ApprovedDate
)z on z.Nip = a.Nip
where a.flag = 1
and h.AreaCode like '%%'
and Periode like '%CO-2016-9-16-15%'
and tanggal <= k.[to] and tanggal >= k.[from] order by i.Description asc) nilai
pivot ( sum(nilai) for row in ([1],[2],[3],[4],[5]) ) piv)A order by Area;
i get this.

Your last script retrievs the row with the minimum ApprovedDate. It should be effectively the same as
SELECT ApprovedDate, NewPosition
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY NewPosition ORDER BY ApprovedDate asc) AS rownumber, ApprovedDate, NewPosition
FROM historyposition
WHERE nip = '1004863') t
WHERE rownumber = 1
Try to use this snippet in your bigger query.
EDIT
Detect first group of NewPosition when historyposition is odered by ApprovedDate desc and take first row witn min ApprovedDate in group.
with HistoryPosition as(
-- sample data
select sKodeMutasiP,OldPosition,NewPosition,ApprovedBy
,cast(ApprovedDate as DATE) ApprovedDate, Nip,KodePenilaian
from (
values
('HP0000514','P007','P007',0802678,'2016-09-15',1004863,'PE0000787')
,('HP0000513','P007','P007',0802678,'2016-04-04',1004863,'PE0000130')
,(NULL,NULL,'P007',NULL,'2016-04-04',1004863,NULL)
,(NULL,NULL,'P041',NULL,'2016-01-20',1004863,NULL)
,(NULL,NULL,'P007',NULL,'2015-02-12',1004863,NULL)
) t (sKodeMutasiP,OldPosition,NewPosition,ApprovedBy,ApprovedDate,Nip,KodePenilaian)
)
select top(1) NewPosition, ApprovedDate
from (
select NewPosition,ApprovedDate
, grp = row_number() over(order by ApprovedDate desc) - row_number() over(partition by NewPosition order by ApprovedDate desc)
from HistoryPosition
where nip = 1004863
) t
where grp = 0
order by ApprovedDate;
EDIT2
Use this snippet in OUTER APPLY instead of LEFT JOIN as it depends on other table data:
...
outer apply(
select top(1) NewPosition, ApprovedDate
from (
select NewPosition,ApprovedDate
, grp = row_number() over(order by ApprovedDate desc) - row_number() over(partition by NewPosition order by ApprovedDate desc)
from HistoryPosition hp
where hp.nip = a.nip -- other table
) t
where grp = 0
order by ApprovedDate
) npa
...

If your question is, how can I combine two result sets so that they appear in a single row, the following select can be used:
-- first select
select 1 one, 2 two, 3 three, 0 joinCondition
-- second select
select 'a' a, 'b' b, 'c' c, 0 joinCondition
-- combined
select one, two, three, a, b, c
from (select 1 one, 2 two, 3 three, 0 joinCondition) first
inner join (select 'a' a, 'b' b, 'c' c, 0 joinCondition) second
on first.joinCondition = second.joinCondition
The idea is, we add a dummy column to both selects with equal value so that it can be used to join them

Related

Merge two SELECT queries with different ORDER BY

I have a Story table, and I need the following query:
- first 6 rows sorted by distance (I calculate it)
- next rows are sorted by TIME property descending
declare #profileID int
set #profileID = 6
declare #longitude float
set #longitude = 17.6009169090776
declare #latitude float
set #latitude = 46.9548404806283
SELECT
first.*
FROM
(
SELECT top 6
[dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON
[dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE
[dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
ORDER BY
[DistanceFromUser] asc
) first
UNION ALL
SELECT
last.*
FROM
(
SELECT TOP 100 PERCENT
[dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON
[dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE
[dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
**ORDER BY
Time desc**
) last
My problem is the second query. It not sorts the records after the 6'th row descending by the TIME property, it sorts as ascending.
thnx
Try This
SELECT
first.*
FROM
(
SELECT top 6
[dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
,1 as ord
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON
[dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE
[dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
ORDER BY
[DistanceFromUser] asc
) first
UNION ALL
SELECT
last.*
FROM
(
SELECT TOP 100 PERCENT
[dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
,row_number() over(order by Time desc) as ord
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON
[dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE
[dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
**ORDER BY
Time desc**
) last
My try (example)
declare #ta as table
(
id int
,na varchar(100)
,sal numeric(18,2)
)
insert into #ta( id,na,sal) values (1,'aa',10)
insert into #ta( id,na,sal) values (3,'bb',100)
insert into #ta( id,na,sal) values (2,'c',5)
insert into #ta( id,na,sal) values (4,'dd',50)
select * from
(select top 2 * , 1 as ord from #ta order by id) as f
union all
select * from (select top 100 percent * , row_number() over(order by sal desc) ord from #ta order by sal desc
) as tt
If you want the result set ordered in a particular way, then the outermost SELECT needs an ORDER BY.
You can control the ordering by including multiple keys in the outer ORDER BY. If I'm reading the query correctly, the only difference is the order by, so put the logic for the queries in a CTE:
WITH sf as (
SELECT s.*,
SQRT( POWER(#Longitude - s.[Longitude], 2) + POWER(#Latitude - s.[Latitude], 2)) as [DistanceFromUser]
FROM [dbo].[Follow] f LEFT JOIN
[dbo].[Story] s
ON f.[Followed] = s.[ProfileID]
WHERE f.[Follower] = #ProfileID and
s.IsDraft = 0
)
SELECT sf.*
FROM ((SELECT TOP (6) sf.*, 1 as ord
FROM sf
ORDER BY [DistanceFromUser] ASC
) UNION ALL
(SELECT TOP (6) sf.*, 2 as ord
FROM sf
ORDER BY Time DESC
)
) sf
ORDER BY ord,
(CASE WHEN ord = 1 THEN DistanceFromUser END) ASC,
(CASE WHEN ord = 2 THEN Time END) DESC;
You can also do this with window functions:
WITH sf as (
SELECT s.*,
SQRT( POWER(#Longitude - s.[Longitude], 2) + POWER(#Latitude - s.[Latitude], 2)) as [DistanceFromUser]
FROM [dbo].[Follow] f LEFT JOIN
[dbo].[Story] s
ON f.[Followed] = s.[ProfileID]
WHERE f.[Follower] = #ProfileID and
s.IsDraft = 0
)
SELECT sf.*
FROM (SELECT sf.*,
ROW_NUMBER() OVER (ORDER BY DistanceFromUser) as seqnum_d,
ROW_NUMBER() OVER (ORDER BY Time DESC) as seqnum_t
FROM sf
)
) sf
WHERE seqnum_d <= 6 OR seqnum_t <= 6
ORDER BY ord,
(CASE WHEN seqnum_d <= 6 THEN DistanceFromUser END) ASC,
(CASE WHEN seqnum_t <= 6 THEN Time END) DESC;
Your version could include the same row twice. This version will not duplicate rows that are in the top 6 for both conditions.
So I ended with next solution thnx to everyone
WITH TempTable as
(
SELECT [dbo].[Story].*,
SQRT( POWER(#Longitude - [dbo].[Story].[Longitude], 2) + POWER(#Latitude - [dbo].[Story].[Latitude], 2)) as [DistanceFromUser]
FROM
[dbo].[Follow]
LEFT JOIN
[dbo].[Story]
ON [dbo].[Follow].[Followed] = [dbo].[Story].[ProfileID]
WHERE [dbo].[Follow].[Follower] = #ProfileID and
[dbo].[Story].IsDraft = 0
)
SELECT
first.*
FROM
(
SELECT top 6
*,
1 as ord
FROM
TempTable
ORDER BY
[DistanceFromUser] asc
) first
UNION ALL
SELECT
last.*
FROM
(
SELECT TOP 100 PERCENT
*,
row_number() over(order by Time desc) as ord
FROM
TempTable
ORDER BY
Time desc
) last

SQL SERVER Update and Insert to different table that has duplicate value

I have two tables, Table A and B. I want to update the RAM_PARENT_CATEGORY_CODE column of Table A with the value from Table B. However, for category_alert_ctr 55 and 82. There are 2 possible RAM_PARENT_CATEGORY_CODE from Table B.
I want to insert a new row in Table A to accommodate both of those RAM_PARENT_CATEGORY_CODE (CRM1b and crm1c). Then update the value with the correct value from Table B.
So the expected result will be :
55 | crm1b1 | CRM1 | CRM1b
83 | crm1b1 | CRM1 | crm1c
82 | Area1 | BuildApp | OFC01
84 | Area1 | BuildApp | OFC02
Do you have any idea to do this? Thanks.
Use the MERGE statement to insert such rows from Table B that are not in Table A, and update those that are in Table A.To check, click here.
with
a as (
select
*,
row_number() over(
partition by category_alert_ctr order by (select 0)
) rn
from #A
),
b as (
select
*,
row_number() over(
partition by RAM_CATEGORY_CODE, RAM_GROUP
order by RAM_PARENT_CATEGORY_CODE
) rn,
row_number() over(
order by RAM_CATEGORY_CODE, RAM_PARENT_CATEGORY_CODE
) cac_offset
from #B
),
m as (
select
a.category_alert_ctr,
iif(b.rn = 1 and a.category_alert_ctr is not null,
a.category_alert_ctr,
max(a.category_alert_ctr) over() + b.cac_offset) new_cac,
b.RAM_CATEGORY_CODE, b.RAM_GROUP, b.RAM_PARENT_CATEGORY_CODE, b.rn
from b
left join a
on b.RAM_CATEGORY_CODE = a.ram_category_code and
b.RAM_GROUP = a.RAM_GROUP and b.rn = a.rn
)
merge a
using m
on a.RAM_CATEGORY_CODE = m.RAM_CATEGORY_CODE and
a.RAM_GROUP = m.RAM_GROUP and a.rn = m.rn
when matched then
update set
a.category_alert_ctr = m.new_cac,
a.RAM_PARENT_CATEGORY_CODE = m.RAM_PARENT_CATEGORY_CODE
when not matched then
insert(category_alert_ctr, ram_category_code,
RAM_GROUP, RAM_PARENT_CATEGORY_CODE)
values(m.new_cac, m.RAM_CATEGORY_CODE,
m.RAM_GROUP, m.RAM_PARENT_CATEGORY_CODE)
;
to handle multiple matches, use row_number() to generate a unique numbering for matching
UPDATE A
SET RAM_PARENT_CATEGORY_CODE = B.RAM_PARENT_CATEGORY_CODE
FROM (
SELECT *, RN = ROW_NUMBER() OVER (PARTITION BY RAM_CATEGORY_CODE, RAM_GROUP
ORDER BY RAM_CATEGORY_CODE)
FROM TABLEA
) A
INNER JOIN (
SELECT *, RN = ROW_NUMBER() OVER (PARTITION BY RAM_CATEGORY_CODE, RAM_GROUP
ORDER BY RAM_CATEGORY_CODE)
FROM TABLEB
) B
ON A.RAM_CATEGORY_CODE = B.RAM_CATEGORY_CODE
AND A.RAM_GROUP = B.RAM_GROUP
AND A.RN = B.RN
If necessary to update the category_alert_ctr column.To check, click here.
declare #A table(
category_alert_ctr int,
ram_category_code varchar(30),
RAM_GROUP varchar(30),
RAM_PARENT_CATEGORY_CODE varchar(30)
);
insert #A values
(25, 'HLTHLIC_FF', 'HEALTHLIC', null),
(28, 'M16', 'QBL', null),
(55, 'crm1b1', 'CRM1', null),
(55, 'crm1b1', 'CRM1', null),
(82, 'Area1', 'BuildApp', null),
(82, 'Area1', 'BuildApp', null);
declare #B table(
RAM_CATEGORY_CODE varchar(30),
RAM_GROUP varchar(30),
RAM_PARENT_CATEGORY_CODE varchar(30)
);
insert #B values
('HLTHLIC_FF', 'HEALTHLIC', 'HLTHLC_RST'),
('M16', 'QBL', 'EPA'),
('crm1b1', 'CRM1', 'CRM1b'),
('crm1b1', 'CRM1', 'crm1c'),
('Area1', 'BuildApp', 'OFC01'),
('Area1', 'BuildApp', 'OFC02');
with
a as (
select
*,
iif(
row_number() over(
partition by category_alert_ctr order by (select 0)
) = 1,
category_alert_ctr,
max(category_alert_ctr) over() +
row_number() over(order by category_alert_ctr) -
dense_rank() over(order by category_alert_ctr)
) new_category_alert_ctr,
row_number() over(
partition by category_alert_ctr order by (select 0)
) rn
from #A
),
b as (
select
*,
row_number() over(
partition by RAM_CATEGORY_CODE, RAM_GROUP
order by RAM_PARENT_CATEGORY_CODE
) rn
from #B
)
update a
set a.category_alert_ctr = a2.new_category_alert_ctr,
a.RAM_PARENT_CATEGORY_CODE = b.RAM_PARENT_CATEGORY_CODE
from a
join a a2
on a.category_alert_ctr = a2.category_alert_ctr and a.rn = a2.rn
join b
on a.RAM_CATEGORY_CODE = a.RAM_CATEGORY_CODE and
a.RAM_GROUP = b.RAM_GROUP and a.rn = b.rn
;
select * from #A;

SQL aggregation of data based on dates

I am sure this is a very stupid question and I am having a dumb moment.
Consider the following basic scenario (this is a very small scenario compared with reality which has many many dimensions and measures):
What I need to get to is the expected output.
So ALL costs between the input_Date and output_date defined in the params are included. However only the latest PID is included- defined as either:
1- where PIDs run sequentially, or overlap the latest one based on date_to as long as both aren't active at the # output date
2- where there are two PID active at the # output date show both
I can't for the life of me work out how to do this in SQL, note that is has to be non dynamic and not use any CTE unfortunately, just your basic SQL with subqueries
Obviously returning the necessary list of ID and PID is easy:
declare #input_date date ='2006-01-01'
declare #output_date date ='2006-12-31'
select a.PID, a.ID
from #tmp a
where date_from <=#output_date and date_to >=#input_date
But I can't figure out how to join this back to return the correct cost values
drop table tmp
CREATE TABLE [dbo].[tmp](
[date_from] [datetime] NOT NULL,
[date_to] [datetime] NOT NULL,
[ID] [nvarchar](25) NOT NULL,
[PID] [nvarchar](25) NOT NULL,
[cost] [float] NULL
) ON [PRIMARY]
INSERT tmp VALUES('2005-1-1','2005-1-31','10001','X123',1254.32)
INSERT tmp VALUES('2000-10-10','2006-8-21','10005','TEST01',21350.9636378758)
INSERT tmp VALUES('2006-8-22','2099-12-31','10005','TEST02',22593.4926163943)
INSERT tmp VALUES('2006-1-1','2099-12-31','10006','X01',22458.3342354444)
INSERT tmp VALUES('2006-2-8','2099-12-31','10006','X02',22480.3772331959)
INSERT tmp VALUES('2006-1-1','2006-2-7','10007','AB01',565.416874152212)
INSERT tmp VALUES('2006-2-8','2006-7-31','10007','AA05',19108.3206482165)
I've made some progress using a CTE so you can see how I would do it this way if I could:
drop table #tmp
CREATE TABLE #tmp (
[date_from] [datetime] NOT NULL,
[date_to] [datetime] NOT NULL,
[ID] [nvarchar](25) NOT NULL,
[PID] [nvarchar](25) NOT NULL,
[cost] [float] NULL
) ON [PRIMARY]
INSERT #tmp VALUES('2005-1-1','2005-1-31','10001','X123',1254.32)
INSERT #tmp VALUES('2000-10-10','2006-8-21','10005','TEST01',21350.9636378758)
INSERT #tmp VALUES('2006-8-22','2099-12-31','10005','TEST02',22593.4926163943)
INSERT #tmp VALUES('2006-1-1','2099-12-31','10006','X01',22458.3342354444)
INSERT #tmp VALUES('2006-2-8','2099-12-31','10006','X02',22480.3772331959)
INSERT #tmp VALUES('2006-1-1','2006-2-7','10007','AB01',565.416874152212)
INSERT #tmp VALUES('2006-2-8','2006-7-31','10007','AA05',19108.3206482165)
declare #input_date date ='2006-01-01'
declare #output_date date ='2006-12-31'
;with cte as (
select t.id,t.PID,t.cost,t.date_from,t.date_to ,
iif(date_To >= #output_date OR max_date_To is not null,PID,NULL) as PID2,
b.total_id_cost
from #tmp t
left join (select ID,max(date_to) as max_date_to
from #tmp
where date_from <=#output_date and date_to >=#input_date
group by ID) a
on t.ID = a.ID and t.date_to = a.max_date_to
left join (Select ID, sum(cost) as total_id_cost
from #tmp
where date_from <=#output_date and date_to >=#input_date
group by ID) b
on t.ID = b.ID
where date_from <=#output_date and date_to >=#input_date )
select distinct ID,PID2,
iif(ID in (
select ID
from cte
where PID2 IS NULL)
and ID not in (select ID
from cte
where PID IS NOT NULL
group by ID
having count (distinct PID2) >1 ), cte.total_id_cost, cost) as cost
from cte
where PID2 is not null;
so it looks like there's several problems to solve within 1 query.
We want the PID that matches the latest date. This wasn't too difficult and can be solved by joining the data with an aggregate of itself that finds the latest date
Where both PID is active i.e. overlapping from and to dates, then both must show. I found this to be more tricky. in the end I did a query to find the ones that do overlap and meet the dates, and did a count on that. then used this count as a criteria for the join on 1. so that it can conditionally pick the PID that matches the latest date
Then finally using the results from above, you can do the sum to get the cost. The resulting query is a bit of a monster, but here it is.
if it doesn't cover other scenarios not detailed, do let me know.
DECLARE #Data TABLE (date_from DATETIME, date_to DATETIME, ID INT, PID NVARCHAR(50), COST MONEY)
INSERT #Data VALUES('2005-1-1','2005-1-31','10001','X123',1254.32)
INSERT #Data VALUES('2000-10-10','2006-8-21','10005','TEST01',21350.9636378758)
INSERT #Data VALUES('2006-8-22','2099-12-31','10005','TEST02',22593.4926163943)
INSERT #Data VALUES('2006-1-1','2099-12-31','10006','X01',22458.3342354444)
INSERT #Data VALUES('2006-2-8','2099-12-31','10006','X02',22480.3772331959)
INSERT #Data VALUES('2006-1-1','2006-2-7','10007','AB01',565.416874152212)
INSERT #Data VALUES('2006-2-8','2006-7-31','10007','AA05',19108.3206482165)
declare #input_date date ='2006-01-01'
declare #output_date date ='2006-12-31'
select
a.ID,
PIDForMaxDateThatMatches.PID,
SUM(a.cost) as cost
from
#Data a
inner join (
-- number of PIDs for dates that overlap grouped by ID
select
a.ID,
-- where there's no overlap then we want the count to be 1 so that later we can use it as condition
COUNT(DISTINCT ISNULL(b.PID,'')) as NumberOfPID
from
#Data a
-- may or may not find overlaps
LEFT JOIN #data b ON
b.date_from <=#output_date and
b.date_to >=#input_date and
a.date_from <= b.date_to and
a.date_to >= b.date_from and
a.ID = b.ID and
a.PID <> b.PID
where
a.date_from <=#output_date and
a.date_to >=#input_date
group by
a.ID) as PIDCountForOverlappingMatches ON
a.ID = PIDCountForOverlappingMatches.ID
left join (
-- get the PID that matches the max date_to
select
DataForMaxDate.ID,
DataForMaxDate.date_from,
DataForMaxDate.date_to,
DataForMaxDate.PID
from
#Data as DataForMaxDate
inner join (
-- get the max date_to that matches the criteria
select
ID,
MAX(date_to) as maxDateTo
from
#Data a
where
date_from <=#output_date and
date_to >=#input_date
group by
ID) as MaxToDatePerID on
DataForMaxDate.ID = MaxToDatePerID.ID and
DataForMaxDate.date_to = MaxToDatePerID.maxDateTo) as PIDForMaxDateThatMatches on
a.ID = PIDForMaxDateThatMatches.ID AND
-- if there's no overlapping dates the PID count would be 1, which we'll take the PID that matches the max(date_to)
-- but if there is overlap, then we want both dates to show, thus the from date must also match before we take the PID
(PIDCountForOverlappingMatches.NumberOfPID = 1 OR a.date_from = PIDForMaxDateThatMatches.date_from)
where
a.date_from <= #output_date and
a.date_to >= #input_date
GROUP BY
a.ID,
PIDForMaxDateThatMatches.PID
ORDER BY
a.ID
EDIT: DB Fiddle http://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=d43cb4b9765da1bca035531e78a2c77d
Results:
ID PID cost
10005 TEST02 43944.4562
10006 X01 22458.3342
10006 X02 22480.3772
10007 AA05 19673.7375
Hello you can try the following query :
select a.resource_id ID, max(a.post_id) PID, SUM(a.cost) Cost
from #tmp a
where date_from <=#output_date and date_to >=#input_date
group by a.resource_id
order by a.resource_id;
I think this might work:
SELECT
t1.ID,
q1.PID,
SUM(t1.cost)
FROM
Table AS t1
JOIN
(
SELECT
q2.ID,
t2.PID
FROM
(
SELECT
ID,
MAX(date_to) AS maxdate
FROM
Table
GROUP BY
ID
) AS q2
JOIN
table AS t2
ON
q2.ID = t2.ID
AND
q2.maxdate = t2.date_to
) AS q1
ON
t1.ID = q1.ID
AND
t1.PID = q1.PID
GROUP BY
t1.ID,
q1.PID
Here is a query without CTE. Idea of query:
1) Find consecutive dates and make different groups within each id
2) Find min and max date, sum of costs for each group
3) Limit by input parametres
declare #date_from date = '20060101'
declare #date_to date = '20061231'
declare #myTable table(
date_from date
, date_to date
, id int
, pid varchar(30)
, cost decimal(10,2)
)
insert into #myTable values
('20050101', '20050201', 10001, 'x123', 1254.32)
, ('20001010', '20060821', 10005, 'test01', 21350.96)
, ('20060822', '20991231', 10005, 'test02', 22593.49)
, ('20060101', '20991231', 10006, 'x01', 22548.33)
, ('20060208', '20991231', 10006, 'x02', 22480.38)
, ('20060101', '20060207', 10007, 'abo1', 565.42)
, ('20060208', '20060731', 10007, 'abo2', 19108.32)
select
date_from = min(date_from), date_to = max(date_to)
, id, pid = max(case when date_to = max_date_to then pid end)
, cost = sum(cost)
from (
select
a.date_from, a.date_to, a.id, a.pid, a.cost, a.rn, grp = sum(b.ss)
, max_date_to = max(a.date_to) over (partition by a.id, sum(b.ss))
from
(
select
a.*, ss = case when datediff(dd, b.date_to, a.date_from) = 1 then 0 else 1 end
from
(
select
*, rn = row_number() over (partition by id order by date_from)
from
#myTable
) a
left join (
select
*, rn = row_number() over (partition by id order by date_from)
from
#myTable
) b on a.id = b.id and a.rn - 1 = b.rn
) a
left join (
select
a.*, ss = case when datediff(dd, b.date_to, a.date_from) = 1 then 0 else 1 end
from
(
select
*, rn = row_number() over (partition by id order by date_from)
from
#myTable
) a
left join (
select
*, rn = row_number() over (partition by id order by date_from)
from
#myTable
) b on a.id = b.id and a.rn - 1 = b.rn
) b on a.id = b.id and a.rn >= b.rn
group by a.date_from, a.date_to, a.id, a.pid, a.cost, a.rn
) t
group by id, grp, max_date_to
having min(date_from) <= #date_from and max(date_to) >= #date_to
order by id
Output
date_from date_to id pid cost
------------------------------------------------
2000-10-10 2099-12-31 10005 test02 43944.45
2006-01-01 2099-12-31 10006 x01 22548.33
Result is a bit different than your provided output. But:
1) For id = 10006 and pid = X02 date_from = 08/02/2006 while input is 01/01/2006
2) For id = 10007 date_to = 31/07/2006 while input is 31/12/2006
So, I think query works correctly
Rextester demo in more readable format with cte

SQL Joining table with Min and Sec Min row

I want to join table 1 with table2 twice becuase I need to get the first minimum record and the second minimum. However, I can only think of using a cte to get the second minimum record. Is there a better way to do it?
Here is the table table:
I want to join Member with output table FirstRunID whose Output value is 1 and second RunID whose Output value is 0
current code I am using:
select memid, a.runid as aRunid,b.runid as bRunid
into #temp
from FirstTable m inner join
(select min(RunID), MemID [SecondTable] where ouput=1 group by memid)a on m.memid=a.memid
inner join (select RunID, MemID [SecondTable] where ouput=0 )b on m.memid=a.memid and b.runid>a.runid
with cte as
(
select row_number() over(partition by memid, arunid order by brunid ),* from #temp
)
select * from cte where n=1
You can use outer apply operator for this:
select * from t1
outer apply(select top 1 t2.runid from t2
where t1.memid = t2.memid and t2.output = 1 order by t2.runid) as oa1
outer apply(select top 1 t2.runid from t2
where t1.memid = t2.memid and t2.output = 0 order by t2.runid) as oa2
You can do this with conditional aggregation. Based on your results, you don't need the first table:
select t2.memid,
max(case when output = 1 and seqnum = 1 then runid end) as OutputValue1,
max(case when output = 0 and seqnum = 2 then runid end) as OutputValue2
from (select t2.*,
row_number() over (partition by memid, output order by runid) a seqnum
from t2
) t2
group by t2.memid;
declare #FirstTable table
(memid int, name varchar(20))
insert into #firsttable
values
(1,'John'),
(2,'Victor')
declare #secondtable table
(runid int,memid int,output int)
insert into #secondtable
values
(1,1,0),(1,2,1),(2,1,1),(2,2,1),(3,1,1),(3,2,0),(4,1,0),(4,2,0)
;with cte as
(
SELECT *, row_number() over (partition by memid order by runid) seq --sequence
FROM #SECONDTABLE T
where t.output = 1
union all
SELECT *, row_number() over (partition by memid order by runid) seq --sequence
FROM #SECONDTABLE T
where t.output = 0 and
t.runid > (select min(x.runid) from #secondtable x where x.memid = t.memid and x.output = 1 group by x.memid) --lose any O output record where there is no prior 1 output record
)
select cte1.memid,cte1.runid,cte2.runid from cte cte1
join cte cte2 on cte2.memid = cte1.memid and cte2.seq = cte1.seq
where cte1.seq = 1 --remove this test if you want matched pairs
and cte1.output = 1 and cte2.output = 0

convert row to column using Pivot without any clause

I have a table like below.
I need to get the data like below.
I have created two temp tables and achieved the result like this. Please help me to do the same with PIVOT.
At least I wouldn't use pivot for that, to my mind this is simpler to do with group by and row_number:
select UserId, max(starttime) as starttime, max(endtime) as endtime
from (
select UserId,
case when StartOrEnd = 'S' then time end as starttime,
case when StartOrEnd = 'E' then time end as endtime,
row_number() over (partition by UserID order by time asc)
+ case when StartOrEnd = 'S' then 1 else 0 end as GRP
from table1
) X
group by UserId, GRP
order by starttime
The derived table splits the time into start / end time columns (to handle cases where only one exists) and uses a trick with row number to group the S / E items together. The outer select just groups the rows into the same row.
Example in SQL Fiddle
Not a efficient solution as JamesZ but should work
create table #tst (userid int,start_end char(1),times datetime)
insert #tst values
(1,'S','07-27-2015 16:45'),
(1,'E','07-27-2015 16:46'),
(2,'S','07-27-2015 16:47'),
(2,'E','07-27-2015 16:48'),
(1,'S','07-27-2015 16:49'),
(1,'E','07-27-2015 16:50')
WITH cte
AS (SELECT Row_number()OVER(ORDER BY times) rn,*
FROM #tst),
cte1
AS (SELECT a.userid,
a.start_end,
a.times,
CASE WHEN a.userid = b.userid THEN 0 ELSE 1 END AS com,
a.rn
FROM cte a
LEFT OUTER JOIN cte b
ON a.rn = b.rn + 1),
cte2
AS (SELECT userid,
start_end,
times,
(SELECT Sum(com)
FROM cte1 b
WHERE b.rn <= a.rn) AS row_num
FROM cte1 a)
SELECT USERID,
starttime=Min(CASE WHEN start_end = 's' THEN times END),
endtime=Max(CASE WHEN start_end = 'e' THEN times END)
FROM cte2
GROUP BY USERID,
row_num
Here is another method
declare #t table(userid int, StartOrEnd char(1), time datetime)
insert into #t
select 1,'S','2015-07-27 16:45' union all
select 1,'E','2015-07-27 16:46' union all
select 2,'S','2015-07-27 16:47' union all
select 2,'E','2015-07-27 16:48' union all
select 1,'S','2015-07-27 16:49' union all
select 1,'E','2015-07-27 16:50'
select userid,min(time) as minimum_time, max(time) as maximum_time from
(
select *, row_number() over (partition by cast(UserID as varchar(10))
+StartOrEnd order by time asc) as sno
from #t
) as t
group by userid,sno
Result
userid minimum_time maximum_time
----------- ----------------------- -----------------------
1 2015-07-27 16:45:00.000 2015-07-27 16:46:00.000
2 2015-07-27 16:47:00.000 2015-07-27 16:48:00.000
1 2015-07-27 16:49:00.000 2015-07-27 16:50:00.000