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
Related
I have a table with sample data below.
PatId NetType
100 In
100 Out
100 NA
101 Out
101 NA
102 NA
103 In
When there are multiple netTypeid for same patient return only top one prioritized by( In,Out,NA) as order. What i am trying to do when there are In/Out/NA available for a patid then should return back only In, when there is Out/NA available for a patid then it should return back only In.If no duplicate just return back as is. Output for above scenario should be
PatId NetType
100 In
101 Out
102 NA
103 In
Use row_number() to order your table by NetType
select
PatId, NetType
from (
select
PatId, NetType
, row_number() over (partition by PatId order by case NetType when 'In' then 1 when 'Out' then 2 else 3 end) rn
from
myTable
) t
where
rn = 1
Similar to uzi
DECLARE #T AS TABLE (PatId int, NetType varchar(20));
insert into #t values
(100, 'In')
, (100, 'Out')
, (100, 'NA')
, (101, 'Out')
, (101, 'NA')
, (102, 'NA')
, (103, 'In');
DECLARE #O AS TABLE (ord int primary key, NetType varchar(20));
insert into #O values (1, 'In'), (2, 'Out'), (3, 'NA');
select tt.PatId, tt.NetType
from ( select t.*
, ROW_NUMBER() over (partition by PatId order by o.ord) as rn
from #t t
join #O o
on t.NetType = o.NetType
) tt
where tt.rn = 1;
I am trying to find Max, min, avg and last value of a column in single query.
Platform: SQL Server 2012
Sample Table:
SN Month Acc Bal
------------------------
1 7 101 1,000/-
2 7 101 1,500/-
3 7 101 1,700/-
4 8 101 1,200/-
5 8 101 900/-
6 9 101 2,500/-
Query I wrote:
select
[Month], [Acc],
min(Bal) as MinBal,
avg(Bal) as AvgBal,
max(Bal) as MaxBal
--, ??? for as LastBal
from
MyTable
Group By
[Month], [Acc]
Query with Last_Value returns all records instead of aggregated records
select
[Month], [Acc],
min(Bal) as MinBal,
avg(Bal) as AvgBal,
max(Bal) as MaxBal,
LAST_VALUE(Bal) OVER (partition by [Acc] order by [Month]) as LastBal
from
MyTable
Group By
[Month], [Acc], Bal
Also including last_value(bal) is generating an error with bal required on group by list
Column 'Bal' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Please try this solution-
DATA Generation
CREATE TABLE Alls
(
SN INT
,[Month] INT
,Acc INT
,Bal INT
)
GO
INSERT INTO Alls VALUES
(1, 7, 101, 1000),
(2, 7, 101, 1500),
(3, 7, 101, 1700),
(4, 8, 101, 1200),
(5, 8, 101, 900),
(6, 9, 101, 2500)
GO
SOLUTION
SELECT sn,Acc,[Month] ,Bal
, MIN(Bal) OVER(PARTITION BY Acc,[Month]) MinBal
, AVG(Bal*1.) OVER(PARTITION BY Acc,[Month]) AvgBal
, MAX(Bal) OVER(PARTITION BY Acc,[Month]) MaxBal
, FIRST_VALUE(Bal) OVER(PARTITION BY Acc,[Month] ORDER BY SN DESC) lastVal
FROM Alls
ORDER By SN
OUTPUT
sn Acc Month Bal MinBal AvgBal MaxBal lastVal
----------- ----------- ----------- ----------- ----------- ---------------- ----------- -----------
1 101 7 1000 1000 1400.000000 1700 1700
2 101 7 1500 1000 1400.000000 1700 1700
3 101 7 1700 1000 1400.000000 1700 1700
4 101 8 1200 900 1050.000000 1200 900
5 101 8 900 900 1050.000000 1200 900
6 101 9 2500 2500 2500.000000 2500 2500
(6 rows affected)
IF you only need acc,month and other aggregate columns then use below-
SOLUTION
SELECT Acc,[Month],MAX(MinBal)MinBal,MAX(AvgBal)AvgBal,MAX(MaxBal)MaxBal,MAX(lastVal)lastVal
FROM
(
SELECT sn,Acc,[Month] ,Bal
, MIN(Bal) OVER(PARTITION BY Acc,[Month]) MinBal
, AVG(Bal*1.) OVER(PARTITION BY Acc,[Month]) AvgBal
, MAX(Bal) OVER(PARTITION BY Acc,[Month]) MaxBal
, FIRST_VALUE(Bal) OVER(PARTITION BY Acc,[Month] ORDER BY SN DESC) lastVal
FROM Alls
)u GROUP BY Acc,[Month]
OUTPUT
Acc Month MinBal AvgBal MaxBal lastVal
----------- ----------- ----------- ---------------- ----------- -----------
101 7 1000 1400.000000 1700 1700
101 8 900 1050.000000 1200 900
101 9 2500 2500.000000 2500 2500
(3 rows affected)
select *
from
( SELECT sn, Acc, [Month], Bal
, MIN(Bal) OVER(PARTITION BY Acc, [Month]) MinBal
, AVG(Bal) OVER(PARTITION BY Acc, [Month]) AvgBal
, MAX(Bal) OVER(PARTITION BY Acc, [Month]) MaxBal
, row_number() OVER(PARTITION BY Acc, [Month] ORDER BY SN DESC) as rn
) tt
where rn = 1
ORDER By sn
You can achieve as below:
select
tt.Month
, tt.Acc
, min(Bal) as MinBal
, avg(Bal) as AvgBal
, max(Bal) as MaxBal
, latest.balance
FROM #tbl1 as tt
JOIN (
SELECT
id
,month
,acc
,bal as balance
FROM #tbl1 AS t1
WHERE id = (SELECT MAX(id)
FROM #tbl1 AS t2
WHERE t1.month = t2.month
AND t1.acc = t2.acc
GROUP BY month, acc)
) as latest
on tt.month = latest.month
AND tt.acc = latest.acc
Group By tt.Month, tt.Acc, latest.balance
DROP TABLE #tbl1
I am trying to get some sorting and keep together (not really grouping) working.
In my sample data I would like to keep the DealerIDs together, sorted by IsPrimaryDealer DESC, but show the group (ok maybe it is grouping) of dealers by the ones with the most recent entry.
Result set 2 is the closest, but Grant and his brother should be displayed as the first two rows, in that order. (Grant should be row 1, Grants Brother row 2 because Grants Brother was the most recently added)
DECLARE #temp TABLE (
DealerPK int not null IDENTITY(1,1), DealerID int,
IsPrimaryDealer bit, DealerName varchar(50), DateAdded datetime
)
INSERT INTO #temp VALUES
(1, 1, 'Bob', GETDATE() - 7),
(2, 1, 'Robert', GETDATE() - 7),
(3, 1, 'Grant', GETDATE() - 7),
(3, 0, 'Grants Brother', GETDATE() - 1),
(2, 0, 'Roberts Nephew', GETDATE() - 2),
(1, 0, 'Bobs Cousin', GETDATE() - 3)
-- Data As Entered
SELECT * FROM #temp
-- Data Attempt at Row Numbering
SELECT *, intPosition =
ROW_NUMBER() OVER (PARTITION BY IsPrimaryDealer ORDER BY DealerID, IsPrimaryDealer DESC)
FROM #temp
ORDER BY DateAdded DESC
-- Data Attempt By DateAdded
SELECT *, intPosition =
ROW_NUMBER() OVER (PARTITION BY DealerID ORDER BY DateAdded DESC)
FROM #temp
ORDER BY intPosition, DateAdded
Expected Result
PK DID IsPr Name DateAdded
3 3 1 Grant 2015-10-08 17:14:26.497
4 3 0 Grants Brother 2015-10-14 17:14:26.497
2 2 1 Robert 2015-10-08 17:14:26.497
5 2 0 Roberts Nephew 2015-10-13 17:14:26.497
1 1 1 Bob 2015-10-08 17:14:26.497
6 1 0 Bobs Cousin 2015-10-12 17:14:26.497
As requested by OP:
;WITH Cte AS(
SELECT *,
mx = MAX(DateAdded) OVER(PARTITION BY DealerID) FROM #temp
)
SELECT *
FROM Cte
ORDER BY mx DESC, DealerID, IsPrimaryDealer DESC
Hope i understood your question,
This query results expected output :
SELECT Row_number()
OVER (
PARTITION BY DealerID
ORDER BY DealerPK)RN,
DealerPK,
DealerID,
IsPrimaryDealer,
DealerName,
DateAdded
FROM #temp
ORDER BY DealerID DESC
This is an Employee table,
Id Name Salary
1 A.J 7000
2 B.S 30000
3 C.K 2000
4 D.O 10000
5 E.L 500
Now i want to display 1st highest salary then minimum salary then 2nd maximum salary then 2nd minimum salaray and so on..up to nth row.
Expected Output,
Id Name Salary
2 B.S 30000
5 E.L 500
4 D.O 10000
3 C.K 2000
1 A.J 7000
One more variant without explicit COUNT. SQL Fiddle.
Try also to add this row to sample data (6, 'X.Y', 7000) in the fiddle. The query still returns correct results.
DECLARE #Employee TABLE (ID int, Name nvarchar(50), Salary money);
INSERT INTO #Employee (ID, Name, Salary) VALUES
(1, 'A.J', 7000),
(2, 'B.S', 30000),
(3, 'C.K', 2000),
(4, 'D.O', 10000),
(5, 'E.L', 500);
WITH
CTE
AS
(
SELECT *, NTILE(2) OVER (ORDER BY Salary, ID) AS n
FROM #Employee AS E
)
SELECT
*
,SIGN(n-1.5) AS s
,SIGN(n-1.5)*Salary AS ss
,ROW_NUMBER() OVER(PARTITION BY n ORDER BY SIGN(n-1.5)*Salary DESC) AS rn
FROM CTE
ORDER BY rn, ss DESC;
Result
ID Name Salary n s ss rn
2 B.S 30000.00 2 1.0 30000.00000 1
5 E.L 500.00 1 -1.0 -500.00000 1
4 D.O 10000.00 2 1.0 10000.00000 2
3 C.K 2000.00 1 -1.0 -2000.00000 2
1 A.J 7000.00 1 -1.0 -7000.00000 3
I left intermediary columns in the output to illustrate how it works.
Using Row_Number() and Count()
Fiddle Demo
declare #count int=(select count(1) from Employee);
with cte1 as
(
select ROW_NUMBER() over(order by salary desc) as rn,0 Sort,Id,Name,Salary, count(Id) over () cnt from Employee
union all
select ROW_NUMBER() over(order by salary) as rn,1 Sort,Id,Name,Salary, count(Id) over () cnt from Employee
)
select top (#count) Id,Name,Salary from cte1 where rn <= (floor(cnt/2) + cnt%2) order by rn,sort
Below is the solution:
--Create dummy employee table
CREATE TABLE tbl_Employee
(
Id INT,
Name VARCHAR(100),
Salary NUMERIC(9, 2)
)
GO
--Insert few dummy rows in the table
INSERT INTO #Employee
(Id, Name, Salary)
VALUES(100, 'John', 7000),
(101, 'Scott', 30000),
(102, 'Jeff', 2000),
(103, 'Jimy', 10000),
(104, 'Andrew', 500),
(105, 'Alister', 100)
GO
--Get data as required
DECLARE #Cnt INT = 0, #SeqLimit INT = 0
SELECT #Cnt = COUNT(1) FROM tbl_employee
SET #SeqLimit = CEILING(#Cnt / 2.0)
SELECT * FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY Salary DESC) AS SEQ, Id, Name, Salary FROM tbl_employee
)DT1
WHERE SEQ <= #SeqLimit
UNION ALL
SELECT * FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY Salary ASC) AS SEQ, Id, Name, Salary FROM tbl_employee
)DT2
WHERE SEQ <= #SeqLimit - (#Cnt % 2)
ORDER BY SEQ ASC, Salary DESC
The same can be achieved with different approaches and here you can find more on this:
http://www.sqlrelease.com/order-max-and-min-value-rows-alternatively-in-sql-server
Sno Water Milk
1 50 100
2 22 120
3 11 142
i have this table.Now i want result like
Sno Type Qnty
1 Water 83
2 Milk 362
How can I please tell me.
SQL Server 2008 does not support this kind of statement.
You can achieve that in 2 ways:
using temporary table (variable type table)
DECLARE #products TABLE(Sno INT, Water INT, Milk INT)
INSERT INTO #products
VALUES (1, 50, 100), (2, 22, 120), (3, 11, 142)
SELECT ROW_NUMBER() OVER(ORDER BY SUM(Qnty)) AS RowNo, Product, SUM(Qnty) AS Qnty
FROM (
SELECT Product, Qnty
FROM (
SELECT *
FROM #products
) AS pvt
UNPIVOT (Qnty FOR Product IN ([Water],[Milk])) AS unpvt
) AS T
GROUP BY Product</pre>
or
;WITH T AS
(
SELECT Sno, Water, Milk
FROM (
SELECT 1 AS Sno, 50 AS Water, 100 AS Milk
UNION ALL
SELECT 2, 22, 120
UNION ALL
SELECT 3, 11, 142
) t (Sno, Water, Milk))
SELECT Sno = ROW_NUMBER() OVER(ORDER BY SUM(Upvt.Qnty)),
upvt.Type,
Qnty = SUM(Upvt.Qnty)
FROM T
UNPIVOT
( Qnty
FOR Type IN ([Water], [Milk])
) upvt
GROUP BY upvt.Type
ORDER BY Qnty;</pre>
Please,refer MSDN documentation.
The first step is to UNPIVOT your data:
WITH T AS
( SELECT Sno, Water, Milk
FROM (VALUES (1, 50, 100), (2, 22, 120), (3, 11, 142)) t (Sno, Water, Milk)
)
SELECT upvt.Sno,
upvt.Type,
Upvt.Qnty
FROM T
UNPIVOT
( Qnty
FOR Type IN ([Water], [Milk])
) AS upvt;
Which will give:
Sno Type Qnty
1 Water 50
1 Milk 100
2 Water 22
2 Milk 120
3 Water 11
3 Milk 142
You can then apply normal aggregation to this result:
WITH T AS
( SELECT Sno, Water, Milk
FROM (VALUES (1, 50, 100), (2, 22, 120), (3, 11, 142)) t (Sno, Water, Milk)
)
SELECT Sno = ROW_NUMBER() OVER(ORDER BY SUM(Upvt.Qnty)),
upvt.Type,
Qnty = SUM(Upvt.Qnty)
FROM T
UNPIVOT
( Qnty
FOR Type IN ([Water], [Milk])
) AS upvt
GROUP BY upvt.Type
ORDER BY Qnty;
Giving:
Sno Type Qnty
1 Water 83
2 Milk 362
EDIT
Based on your comment that you are using SQL Server 2005, not 2008 as indicated below is a full working example that will work on 2005:
DECLARE #T TABLE (Sno INT, Water INT, Milk INT);
INSERT #T (Sno, Water, Milk) VALUES (1, 50, 100);
INSERT #T (Sno, Water, Milk) VALUES(2, 22, 120);
INSERT #T (Sno, Water, Milk) VALUES(3, 11, 142);
SELECT Sno = ROW_NUMBER() OVER(ORDER BY SUM(Upvt.Qnty)),
upvt.Type,
Qnty = SUM(Upvt.Qnty)
FROM #T AS T
UNPIVOT
( Qnty
FOR Type IN ([Water], [Milk])
) AS upvt
GROUP BY upvt.Type
ORDER BY Qnty;
It was the table valued constructor I was using to create the sample data that was causing the error in 2005, nothing to do with the actual query that unpivots then sums the data.
This gives the result you asked for but I get the idea you want something a little more advanced. If so please elaborate.
SELECT 'water', sum(water) FROM table_name
UNION ALL
SELECT 'milk', sum(milk) FROM table_name