Second condition inside SELECT query - sql-server

I have this simple query (TSQL) -
SELECT IdTour, Tour, TourOrder, IdContent
FROM View_ToursListContentId
WHERE (IdContent = 3) OR (IdContent = 4)
ORDER BY TourOrder DESC, Tour
Here is the result
58 Air & Style Tour 2015/16 NULL 3
48 FIS Europa Cup 2015/16 NULL 3
46 FIS World Cup Tour 2015/16 NULL 3
131 KDC Grand Slam Regional Qualifiers 2016 NULL 3
59 Swatch Freeride World Tour 2016 NULL 3
77 World Rookie Tour 2015/16 NULL 3
57 World Snowboard Tour 2015/16 NULL 3
45 X Games 2016 NULL 3
45 X Games 2016 NULL 4
What I need to do is. If ther is inside result IdTour with IdContent = 3 don't show the same IdTour with IdContent = 4. (Show IdTour with IdContent = 4 only, if there is not the same IdTour with IdContent = 3 - in my case show only one IdTour 45 with IdContent 3)
What is the easiest and the fastest way, how could I accomplish it?
Thanx a lot

One way involves using NOT EXISTS:
SELECT IdTour, Tour, TourOrder, IdContent
FROM View_ToursListContentId t1
WHERE (IdContent = 3) OR
((IdContent = 4) AND NOT EXISTS (SELECT 1
FROM View_ToursListContentId t2
WHERE t1.IdTour = t2.IdTour AND
t2.IdContent = 3))
ORDER BY TourOrder DESC, Tour
The NOT EXISTS operator filters out records having IdContent = 4 in case a record with the same IdTour and IdContent = 3 exists.

select * from (
select row_number() over (partition by IdTour, Tour, TourOrder order by IdContent) rownum,
IdTour, Tour, TourOrder, IdContent from View_ToursListContentId)src
where rownum=1

You could use a window function to obtain the lowest IdContent available for that IdTour:
SELECT IdTour,
Tour,
TourOrder,
IdContent,
MinIdContent = MIN(IdContent) OVER(PARTITION BY IdTour)
FROM View_ToursListContentId
WHERE IdContent IN (3, 4);
Then if this is 3 then you select only 3, if it is 4 then you select only 4, or more simply, only select IdContent that matches the minimum:
SELECT IdTour, Tour, TourOrder, IdContent
FROM ( SELECT IdTour,
Tour,
TourOrder,
IdContent,
MinIdContent = MIN(IdContent) OVER(PARTITION BY IdTour)
FROM View_ToursListContentId
WHERE IdContent IN (3, 4)
) AS t
WHERE IdContent = MinIdContent;
FULL EXAMPLE
WITH View_ToursListContentId AS
( SELECT IdTour, Tour, TourOrder, IdContent
FROM (VALUES
(58, 'Air & Style Tour 2015/16', NULL, 3),
(48, 'FIS Europa Cup 2015/16', NULL, 3),
(46, 'FIS World Cup Tour 2015/16', NULL, 3),
(131, 'KDC Grand Slam Regional Qualifiers 2016', NULL, 3),
(59, 'Swatch Freeride World Tour 2016', NULL, 3),
(77, 'World Rookie Tour 2015/16', NULL, 3),
(57, 'World Snowboard Tour 2015/16', NULL, 3),
(45, 'X Games 2016', NULL, 3),
(45, 'X Games 2016', NULL, 4)
) t (IdTour, Tour, TourOrder, IdContent)
)
SELECT IdTour, Tour, TourOrder, IdContent
FROM ( SELECT IdTour,
Tour,
TourOrder,
IdContent,
MinIdContent = MIN(IdContent) OVER(PARTITION BY IdTour)
FROM View_ToursListContentId
WHERE IdContent IN (3, 4)
) AS t
WHERE IdContent = MinIdContent;

you should just use group by IdContent in query
like
SELECT IdTour, Tour, TourOrder, IdContent
FROM View_ToursListContentId
WHERE (IdContent = 3) OR (IdContent = 4)
group by IdContent
ORDER BY TourOrder DESC, Tour

Related

Attendance IN and OUT

Consider the following table structure and sample data -
EmpID InputDateTime StatusINOUT
-------------------------------------
1 2018-05-26 08:44 1
1 2018-05-26 08:44 2
2 2018-05-28 08:44 1
2 2018-05-28 12:44 2
1 2018-05-21 08:44 1
1 2018-05-21 10:44 2
2 2018-05-23 08:44 1
2 2018-05-23 08:44 2
Now I want to separate column InputDateTime into two columns i.e., INTIME(1) and OUTTIME(2). The logic behind this is the date for which StatusInOut is 1 will be InTime and for StatusInOut is 2 that date value will be OUTTIME(2).
The expected output format is as shown below:
Empid INTIME(1) OUTIME(2)
--------------------------------------------
1 2018-05-26 08:44 2018-05-26 08:44
2 2018-05-28 08:44 2018-05-28 12:44
1 2018-05-21 08:44 2018-05-21 10:44
2 2018-05-23 08:44 2018-05-23 08:44
use case when
select empid,max(case when statusINOut=1 then Datetime end) as INtime,
max(case when statusINOut=2 then Datetime end) as Outtime
from table_name t
group by empid,convert(date,Datetime)
Try the following query using join and update.
create table #tempStatus (EmpId int, intTime datetime, sStatus int)
insert into #tempStatus
values(1, '2018-05-26 08:44', 1),
(1, '2018-05-26 08:44', 2),
(2, '2018-05-28 08:44', 1),
(2, '2018-05-28 12:44', 2),
(1, '2018-05-21 08:44', 1),
(1, '2018-05-21 10:44', 2),
(2, '2018-05-23 08:44', 1),
(2, '2018-05-23 08:44', 2)
,(3, '2018-05-23 08:44', 1)
select EmpId, MIN(intTime) as intTime, MAX(intTime) as OutTime into #tempA from (
select EmpId, intTime, intTime as OutTime
from #tempStatus where sStatus = 1
)a
group by EmpId, intTime
update s
set s.OutTime = t.outTime
from #tempA s
left join
(
select EmpId, MAX(outTime) as outTime from(
select EmpId, intTime as outTime
from #tempStatus where sStatus = 2
)b
group by empId,outTime) t
on s.EmpId = t.EmpId and Convert(Varchar,s.OutTime,112) = Convert(Varchar,t.outTime,112)
select * from #tempA order by EmpId
drop table #tempA
DROP TABLE #tempStatus
OR you can also try the below one query
select empid,
max(case when sStatus = 1 then intTime end) as INTIME,
max(case when sStatus = 2 then intTime end) as OUTIME
from (select t.*,
row_number () over ( order by inttime) as seq1,
row_number () over (partition by empid order by inttime) as seq2
from #tempStatus t
) t
group by empid, (seq1-seq2);
Check for the similar answers - here
try:
select EmpID
, min(DateTime) INTIME(1)
, max(DateTime) OUTIME(2)
from TABLE
group by EmpID;

T-SQL Identifying gaps in broken sequence of dates

Please can you help with a problem I am having, I believe is related to the gaps and islands problem in T-SQL. I am using SQL Server 2014.
I am trying to identify count the number of consecutive occurrences of a table/index combination using a date column to distinguish between the broken chains.
Please see the below T-SQL to demonstrate what I am trying to achieve, in particular how do I calculate the Rnk column which for demo purposes I have manually hard-coded?
CREATE TABLE #test (RowID INT IDENTITY(1,1), FileDate DATE, TableName VARCHAR(100), IndexName VARCHAR(100), Rnk INT)
INSERT INTO #test (FileDate, TableName, IndexName, Rnk)
VALUES
('2015-10-31', 't1', 'idx1', 1),
('2015-10-30', 't1', 'idx1', 2),
('2015-10-27', 't1', 'idx1', 1),
('2015-10-26', 't1', 'idx1', 2),
('2015-10-25', 't1', 'idx1', 3),
('2015-10-23', 't1', 'idx1', 1),
('2015-10-22', 't1', 'idx1', 2),
('2015-10-21', 't1', 'idx1', 3),
('2015-10-20', 't1', 'idx1', 4),
('2015-10-19', 't1', 'idx1', 5),
('2015-10-15', 't1', 'idx1', 1),
('2015-10-13', 't1', 'idx1', 1),
('2015-10-10', 't1', 'idx1', 1),
('2015-10-09', 't1', 'idx1', 2),
('2015-10-27', 't3', 'idx13', 1),
('2015-10-26', 't3', 'idx13', 2),
('2015-10-25', 't3', 'idx15', 1),
('2015-10-24', 't3', 'idx15', 2),
('2015-10-21', 't3', 'idx13', 1)
SELECT * FROM #test
DROP TABLE #test
In the screenshot I've attached, the portion of results highlighted would show I want the Rnk column to sequence the consecutive appearance of t1/idx between 2015-10-27 - 2015-10-25, but reset the number for the next appearance at 2015-10-23 through to 2015-10-19.
Can anyone assist me please?
Thanks.
Subtract a sequence of numbers from the date -- and the groups you have identified will have a constant value. Then you can use row_number():
select t.*,
row_number() over (partition by tablename, indexname,
dateadd(day, - seqnum, filedate)
order by filedate desc
) as rnk
from (select t.*,
row_number() over (partition by tablename, indexname order by filedate) as seqnum
from t
) t
I would use cumulative approach :
select t.FileDate, t.TableName, t.IndexName,
row_number() over (partition by tablename, indexname, grp order by rowid)
from (select t.*, sum(case when gap > 1 then 1 else 0 end) over (partition by tablename, indexname order by rowid) as grp
from (select t.*,
isnull(datediff(day, filedate, lag(filedate) over (partition by tablename, indexname order by rowid)), 1) as gap
from #test t
) t
) t;
Similar to the answer from Yogesh, who beat me to it.
(hint: don't expect to be faster when typing an answer on your phone)
SELECT
RowID, FileDate, TableName, IndexName,
ROW_NUMBER() OVER (PARTITION BY TableName, IndexName, DateRank ORDER BY FileDate DESC) AS Rnk
FROM
(
SELECT *,
SUM(DateGap) OVER (PARTITION BY TableName, IndexName ORDER BY FileDate DESC) AS DateRank
FROM
(
SELECT RowID, FileDate, TableName, IndexName,
-- Rnk as ExpRnk,
CASE WHEN DATEDIFF(DAY, FileDate, LAG(FileDate) OVER (PARTITION BY TableName, IndexName ORDER BY FileDate DESC)) <= 1 THEN 0 ELSE 1 END AS DateGap
FROM #Test
) q1
) q2
ORDER BY RowID;

Group up rows based on date overlapping

In a same id, if any of row's effective date and enddate overlaps then we need group it up in a unique id
In below image dategroup is the desired output column
Data is sorted in order by ID asc, EffectiveDate ASC, EndDate Desc
CREATE TABLE #DataTable (id int , EffectiveDate datetime, Enddate Datetime )
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-01 00:00:00.000' AS DateTime), CAST(N'2017-01-11 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-02 00:00:00.000' AS DateTime), CAST(N'2017-01-05 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-03 00:00:00.000' AS DateTime), CAST(N'2017-01-12 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-06 00:00:00.000' AS DateTime), CAST(N'2017-01-09 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (1, CAST(N'2017-01-13 00:00:00.000' AS DateTime), CAST(N'2017-01-19 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (2, CAST(N'2017-02-01 00:00:00.000' AS DateTime), CAST(N'2017-02-11 00:00:00.000' AS DateTime))
GO
INSERT [dbo].#DataTable ([id], [EffectiveDate], [Enddate]) VALUES (2, CAST(N'2017-02-06 00:00:00.000' AS DateTime), CAST(N'2017-02-16 00:00:00.000' AS DateTime))
GO
Try this, Hope it helps. Not the most attractive code but it should work. I may clean it up later if I find some time.
;WITH cte_StepOne as
(
SELECT ROW_NUMBER() OVER (ORDER BY a.[id],
a.[EffectiveDate],
a.[Enddate]) AS SeqNo,
a.[id],
a.[EffectiveDate],
a.[Enddate],
b.[id] AS OverLapID,
b.[EffectiveDate] AS [OverLapEffectiveDate],
b.[Enddate] AS [OverLapEnddate]
FROM ##DataTable a
LEFT JOIN ##DataTable b
ON a.EffectiveDate BETWEEN b.EffectiveDate
AND b.EndDate
AND a.EffectiveDate <> b.EffectiveDate
AND a.EndDate <> b.EndDate --and a.ID <> b.ID
)
,cte_StepTwo AS
(
SELECT SeqNo,
id,
EffectiveDate,
Enddate,
LEAD(OverLapEffectiveDate, 1) OVER (ORDER BY SeqNo) AS LeadValue,LAG(id, 1) OVER (ORDER BY SeqNo) AS LeadValueID,
OverLapID,
OverLapEffectiveDate,
OverLapEnddate
FROM cte_StepOne
)
,cte_Result AS
(
SELECT id,
EffectiveDate,
Enddate,
CASE
WHEN LeadValue = EffectiveDate AND OverLapEffectiveDate IS NULL THEN ID
WHEN OverLapID IS NULL THEN LeadValueID + 1
ELSE OverLapID
END AS OverLapID,
CASE
WHEN LeadValue = EffectiveDate AND OverLapEffectiveDate IS NULL THEN EffectiveDate
ELSE OverLapEffectiveDate
END AS OverLapEffectiveDate,
CASE
WHEN LeadValue = EffectiveDate AND OverLapEffectiveDate IS NULL THEN Enddate
ELSE OverLapEnddate
END AS OverLapEnddate
FROM cte_StepTwo
)
SELECT DISTINCT id,
EffectiveDate,
Enddate,
DENSE_RANK() OVER (ORDER BY ID,OverLapID) AS DateGroup
FROM cte_Result
ORDER BY id,EffectiveDate
Result:
This answer takes the approach of trying to identify records for which the running DateGroup counter should be incremented. Ultimately, we will assign a value of 1 to such records. With this assignment in hand, we can then simply take a cumulative sum to generate the DateGroup.
-- this CTE identifies all new ID records
WITH cte1 AS (
SELECT t.ID, t.EffectiveDate, t.EndDate
FROM
(
SELECT ID, EffectiveDate, EndDate,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY EffectiveDate) rn
FROM yourTable
) t
WHERE t.rn = 1
),
-- this CTE identifies all records whose both effective and end dates
-- do not fall within the range of the start ID record
cte2 AS (
SELECT t1.ID, t1.EffectiveDate, t1.EndDate
FROM yourTable t1
INNER JOIN cte1 t2
ON t1.ID = t2.ID AND
t1.EffectiveDate NOT BETWEEN t2.EffectiveDate AND t2.EndDate AND
t1.EndDate NOT BETWEEN t2.EffectiveDate AND t2.EndDate
),
-- this CTE returns the original table with a new column, amount, which
-- contains a value of 1 should that record cause the DateGroup to be
-- incremented by 1
cte3 AS (
SELECT t1.ID, t1.EffectiveDate, t1.EndDate,
CASE WHEN t2.ID IS NOT NULL OR t3.ID IS NOT NULL THEN 1 ELSE 0 END AS amount
FROM yourTable t1
LEFT JOIN cte1 t2
ON t1.ID = t2.ID AND
t1.EffectiveDate = t2.EffectiveDate AND
t1.EndDate = t2.EndDate
LEFT JOIN cte2 t3
ON t1.ID = t3.ID AND
t1.EffectiveDate = t3.EffectiveDate AND
t1.EndDate = t3.EndDate
)
-- finally, take a cumulative sum of the 'amount' column to generate the DateGroup
SELECT t1.ID,
t1.EffectiveDate,
t1.EndDate,
SUM(t2.amount) AS DateGroup
FROM cte3 t1
INNER JOIN cte3 t2
ON t1.ID >= t2.ID AND
t1.EffectiveDate >= t2.EffectiveDate
GROUP BY t1.id, t1.EffectiveDate, t1.EndDate;
Output:
Demo here:
Rextester
Data used:
CREATE TABLE yourTable (ID int, EffectiveDate datetime, EndDate datetime);
INSERT INTO yourTable
VALUES
(1, '2017-01-01 00:00:00.000', '2017-01-11 00:00:00.000'),
(1, '2017-01-02 00:00:00.000', '2017-01-05 00:00:00.000'),
(1, '2017-01-03 00:00:00.000', '2017-01-12 00:00:00.000'),
(1, '2017-01-06 00:00:00.000', '2017-01-09 00:00:00.000'),
(1, '2017-01-13 00:00:00.000', '2017-01-19 00:00:00.000'),
(2, '2017-02-01 00:00:00.000', '2017-02-11 00:00:00.000'),
(2, '2017-02-06 00:00:00.000', '2017-02-16 00:00:00.000');
What about this? It's simpler that other solutions posted:
WITH
CTE_GetFirstRecordForEachId AS
(
SELECT
id,
EffectiveDate,
Enddate,
rn = ROW_NUMBER() OVER (PARTITION BY id ORDER BY EffectiveDate, EndDate)
FROM
#DataTable
),
CTE_GetOutOfDateRange AS
(
SELECT
a.*,
OutOfDateRange =
CASE WHEN (b.EffectiveDate>=a.EffectiveDate AND b.EffectiveDate<=b.Enddate) OR (b.Enddate>=a.EffectiveDate AND b.Enddate<=b.Enddate)
THEN 0
ELSE 1
END
FROM
#DataTable a
INNER JOIN
CTE_GetFirstRecordForEachId b ON a.id = b.id AND b.rn=1
)
SELECT
id,
Effectivedate,
Enddate,
DateGroup = DENSE_RANK() OVER (ORDER BY id, OutOfDateRange)
FROM
CTE_GetOutOfDateRange
ORDER BY
id, Effectivedate, Enddate
Output:
id Effectivedate Enddate DateGroup
----------- ----------------------- ----------------------- --------------------
1 2017-01-01 00:00:00.000 2017-01-11 00:00:00.000 1
1 2017-01-02 00:00:00.000 2017-01-05 00:00:00.000 1
1 2017-01-03 00:00:00.000 2017-01-12 00:00:00.000 1
1 2017-01-06 00:00:00.000 2017-01-09 00:00:00.000 1
1 2017-01-13 00:00:00.000 2017-01-19 00:00:00.000 2
2 2017-02-01 00:00:00.000 2017-02-11 00:00:00.000 3
2 2017-02-06 00:00:00.000 2017-02-16 00:00:00.000 3
What about this (I am still testing it)
WITH Z AS
(SELECT * FROM (SELECT ID, [EffectiveDate], ENDDate
, LAG(ID) OVER (PARTITION BY ID ORDER BY EffectiveDate, ENDDate Desc) AS ID_Prec
, LAG(EffectiveDate) OVER (PARTITION BY ID ORDER BY EffectiveDate, ENDDate Desc) AS EffDate_Prec
, LAG(ENDDate) OVER (PARTITION BY ID ORDER BY EffectiveDate, ENDDate Desc) AS EndDate_Prec
, ROW_NUMBER() OVER (ORDER BY ID, EffectiveDate,ENDDate DESC) AS RN
, 1 AS DATEGROUP
FROM #DataTable ) C WHERE RN = 1
UNION ALL
SELECT A.ID, A.EffectiveDate, A.Enddate
, A.ID_Prec, A.EffDate_Prec
, A.EndDate_Prec
, A.RN
, CASE WHEN A.ID = A.ID_PREC AND (A.EffectiveDate <=A.EndDate_Prec /* OR A.EndDate>=A.EffDate_Prec*/) THEN Z.DATEGROUP
ELSE Z.DATEGROUP+1 END AS DATEGROUP
FROM (SELECT A.ID, A.EffectiveDate, A.ENDDate
, LAG(A.ID) OVER (PARTITION BY A.ID ORDER BY A.EffectiveDate, A.ENDDate Desc) AS ID_Prec
, LAG(A.EffectiveDate) OVER (PARTITION BY A.ID ORDER BY A.EffectiveDate, A.ENDDate Desc) AS EffDate_Prec
, LAG(A.ENDDate) OVER (PARTITION BY A.ID ORDER BY A.EffectiveDate, A.ENDDate Desc) AS EndDate_Prec
, ROW_NUMBER() OVER (ORDER BY A.ID, A.EffectiveDate,A.ENDDate DESC) AS RN
, 1 AS DATEGROUP
FROM #DataTable A) A
INNER JOIN Z ON A.RN -1= Z.RN
)
SELECT ID, EffectiveDate, Enddate, DATEGROUP FROM Z
Output:
ID EffectiveDate Enddate DATEGROUP
----------- ----------------------- ----------------------- -----------
1 2017-01-01 00:00:00.000 2017-01-11 00:00:00.000 1
1 2017-01-02 00:00:00.000 2017-01-05 00:00:00.000 1
1 2017-01-03 00:00:00.000 2017-01-12 00:00:00.000 1
1 2017-01-06 00:00:00.000 2017-01-09 00:00:00.000 1
1 2017-01-13 00:00:00.000 2017-01-19 00:00:00.000 2
2 2017-02-01 00:00:00.000 2017-02-11 00:00:00.000 3
2 2017-02-06 00:00:00.000 2017-02-16 00:00:00.000 3
guess you are missing some test scenario in your sample date.
;with CTE as
(
select *,ROW_NUMBER()over(order by id, effectivedate)rn
from #DataTable
)
,CTE1 AS
(
select *, 1 New_ID
from cte
where rn=1
union ALL
select c.id,c.effectivedate,c.enddate,c.rn
,case when c.effectivedate between c1.effectivedate
and c1.enddate
and c.id=c1.id then c1.New_ID
else c1.New_ID+1
END
from cte c
inner join cte1 c1
on c.rn=c1.rn+1
and c.rn>1 and c.rn<=7
)
select * from cte1
drop table #DataTable
this may help you. I posted here shortest and simplest version of tsql...
WITH CTE AS (
SELECT *,
ISNULL(LAG(EffectiveDate) OVER (PARTITION BY id ORDER BY id,EffectiveDate,Enddate),EffectiveDate) AS PreviousEffDate,
ISNULL(LAG(Enddate) OVER (PARTITION BY id ORDER BY id,EffectiveDate,Enddate),Enddate) AS PreviousEndDate
FROM #DataTable)
SELECT id,
EffectiveDate,
Enddate,
DENSE_RANK() OVER (ORDER BY id,CASE
WHEN EffectiveDate BETWEEN PreviousEffDate AND PreviousEndDate OR
Enddate BETWEEN PreviousEffDate AND PreviousEndDate OR
PreviousEffDate BETWEEN EffectiveDate AND Enddate OR
PreviousEndDate BETWEEN EffectiveDate AND Enddate
THEN 0
ELSE 1
END) AS DateGroup
FROM CTE
Result:
Have got this one from another forum; altered as per my requirement . looks simple and effective.
WITH C1 AS (
SELECT *,
CASE WHEN EffectiveDate <= MAX(ISnull(EndDate,'9999-12-31 00:00:00.000')) OVER(partition by id ORDER BY EffectiveDate ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) THEN 0 ELSE 1 END AS isstart
FROM #DataTable
)
SELECT ID,EffectiveDate,EndDate,
SUM(isstart) OVER(ORDER BY ID ROWS UNBOUNDED PRECEDING) AS DG
FROM C1

Selecting last 2 rows of each dense_rank

Using MSSQL, I am trying to get some information from a journal where one event happens directly after another event.
So what I am effectively aiming for, is to get a row number partitioned by a TransactionID, and then I need the last 2 rows (last 2 row number) for EACH transactionID (Ordered by a TxnDate field). There could be any number of rows per TransactionID.
So I would get:
JnlId TxnId RowNum
5 10001 65
2 10001 66
10 10002 11
8 10002 12
5 10003 15
98 10003 16
Any ideas how I could achieve this as I am at a loss! The end game after this is to filter out the 'JnlId' field for a select few of IDs.
Bit of a back story. This customer thinks their staff is stealing, so I need to filter out when they are cancelling items directly before finishing off each transaction.
Try this, I added some extra rows to make dense rank more obvious:
Test data:
DECLARE #t table(JnlId int,TxnId int,RowNum int, TxnDate date)
INSERT #t values
(5, 10001,65, '2015-01-01'),
(2, 10001,66, '2015-01-02'),
(2, 10001,66, '2015-01-03'),
(2, 10001,66, '2015-01-04'),
(2, 10001,67, '2015-01-04'),
(2, 10001,67, '2015-01-04'),
(10,10002,11, '2015-01-03'),
(8, 10002,12, '2015-01-04'),
(5, 10003,15, '2015-01-05'),
(98,10003,16, '2015-01-06')
Query:
;WITH CTE AS
(
SELECT
DENSE_RANK() over(partition by txnID order by TxnDate desc) rn,
JnlId, TxnId, RowNum, TxnDate
FROM #t
)
SELECT JnlId, TxnId, RowNum, TxnDate FROM CTE
WHERE rn<=2
Result:
JnlId TxnId RowNum TxnDate
2 10001 66 2015-01-04
2 10001 67 2015-01-04
2 10001 67 2015-01-04
2 10001 66 2015-01-03
8 10002 12 2015-01-04
10 10002 11 2015-01-03
98 10003 16 2015-01-06
5 10003 15 2015-01-05
Instead of ordering in ascending order try descending order
select * from
(
select dense_rank() over(partition by transactionID Order by TxnDate Desc) Rn,*
from yourtable
) A
where rn<=2
Just order by RowNum descending and then select what has ROW_NUMBER less or equals 2
DECLARE #Table TABLE
(
JnlId INT
, TxnId INT
, RowNum INT
);
INSERT INTO #Table
(JnlId, TxnId, RowNum)
VALUES
(5, 10001, 65)
, (2, 10001, 66)
, (10, 10002, 11)
, (8, 10002, 12)
, (5, 10003, 15)
, (98, 10003, 16);
SELECT T.JnlId, T.TxnId, T.RowNum
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY TxnId ORDER BY RowNum DESC) AS RowNo, *
FROM #Table) AS T
WHERE T.RowNo <=2

SQL server: convert rows to columns

I have a table with columns sales(int), month(int). I want to retrieve sum of sales corresponding to every month. I need ouput in form of 12 columns corresponding to each month in which there will be a single record containing sales for for each column(month).
You should take a look at PIVOT for switching rows with columns. This prevents a select statement for each month. Something like this:
DECLARE #salesTable TABLE
(
[month] INT,
sales INT
)
-- Note that I use SQL Server 2008 INSERT syntax here for inserting
-- multiple rows in one statement!
INSERT INTO #salesTable
VALUES (0, 2) ,(0, 2) ,(1, 2) ,(1, 2) ,(2, 2)
,(3, 2) ,(3, 2) ,(4, 2) ,(4, 2) ,(5, 2)
,(6, 2) ,(6, 2) ,(7, 2) ,(8, 2) ,(8, 2)
,(9, 2) ,(10, 2) ,(10, 2) ,(11, 2) ,(11, 2)
SELECT [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11]
FROM
(
SELECT [month], sales
FROM #salesTable
) AS SourceTable
PIVOT
(
SUM(sales)
FOR [month] IN ([0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11])
) AS PivotTable
Not pretty... but this works well
SELECT
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 1) [Sales1],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 2) [Sales2],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 3) [Sales3],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 4) [Sales4],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 5) [Sales5],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 6) [Sales6],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 7) [Sales7],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 8) [Sales8],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 9) [Sales9],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 10) [Sales10],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 11) [Sales11],
(SELECT SUM(Sales) FROM SalesTable WHERE [Month] = 12) [Sales12]
Here's an alternate way to write the pivot that gives you a little more control (especially over the column names). It's also a little easier to generate dynamic SQL for.
It's similar to Robin's answer, but has the advantage of only hitting the table once:
select
Sales1 = sum( case when Month = 1 then Sales end )
, Sales2 = sum( case when Month = 2 then Sales end )
, Sales3 = sum( case when Month = 3 then Sales end )
-- etc..
from SalesTable;
I did some investigation, and it seems like the new pivot operator is just syntax sugar for this type of query. The query plans end up looking identical.
As an interesting aside, the unpivot operator seems to also just be syntax sugar. For example:
If you have a table like:
Create Table Sales ( JanSales int, FebSales int, MarchSales int...)
You can write:
select unpivoted.monthName, unpivoted.sales
from Sales s
outer apply (
select 'Jan', JanSales union all
select 'Feb', FebSales union all
select 'March', MarchSales
) unpivoted( monthName, sales );
And get the unpivoted data...
You can do it with OLAP. Here is another link to MSDN documentation on the topic.
With OLAP, you can create a cube with the information you have, with the layout you need.
If you do not want to go that way, you will have to create summary tables with .NET, Java, TransacSQL, or your preferred language to manipulate SQLServer data.
To easily transpose columns into rows with its names you should use XML. In my blog I was described this with example: Link

Resources