Using Group By to get Desired Result - sql

I have a table "Table1" like below:
EffectiveDate Client Fund RunDate UserId
2014-05-31 A AGG 2014-06-03 user
2014-03-31 A AGG 2014-07-01 user
2014-10-31 A AGG 2014-11-04 user
2014-09-30 A EFA 2013-10-10 user
2014-11-31 A EFA 2014-01-15 user
2014-01-31 A EFA 2014-02-03 user
I need to get result like if Effective Date is maximum for any given input date then it'll only return that record for a particular Fund.I am using query to get desired result like :
SELECT Max(tbl.effectivedate) AS EffectiveDate,
tbl.client,
tbl.fund,
tbl.rundate,
tbl.userid
FROM (SELECT effectivedate,
client,
fund,
rundate,
userid
FROM Table1
WHERE effectivedate < = '11/01/2014') AS tbl
GROUP BY tbl.client,
tbl.fund,
tbl.rundate,
tbl.userid
But I am not getting the desired result. Please if anyone help me.
Desired Output :
EffectiveDate Client Fund RunDate UserId
2014-10-31 A AGG 2014-11-04 user
2014-09-30 A EFA 2013-10-10 user

Try this one:
SAMPLE DATA
create table #table1(
EffectiveDate date,
Client varchar(100),
Fund varchar(100),
RunDate date,
UserId varchar(100)
)
insert into #table1
select '2014-05-31', 'A', 'AGG', '2014-06-03', 'user' union all
select '2014-03-31', 'A', 'AGG', '2014-07-01', 'user' union all
select '2014-10-31', 'A', 'AGG', '2014-11-04', 'user' union all
select '2014-09-30', 'A', 'EFA', '2013-10-10', 'user' union all
select '2014-11-30', 'A', 'EFA', '2014-01-15', 'user' union all
select '2014-01-31', 'A', 'EFA', '2014-02-03', 'user'
CTE SOLUTION
;with cte as(
select
*,
rn = row_number() over(partition by Fund order by EffectiveDate desc)
from #table1
where
EffectiveDate <= '2014/11/01'
)
select
EffectiveDate,
Client,
Fund,
RunDate,
UserId
from cte
where
rn = 1
WITHOUT CTE AND ROW_NUMBER()
select
t1.*
from #table1 t1
inner join (
select
EffectiveDate = MAX(EffectiveDate),
Fund
from #table1
where
EffectiveDate <= '2014/11/01'
group by Fund
) t2
on t2.fund = t1.fund
and t2.EffectiveDate = t1.EffectiveDate

Another way to do it if you are interested. You were almost there with your original code (fiddle here: http://sqlfiddle.com/#!3/20926c/9):
SELECT effectivedate,
client,
fund,
rundate,
userid
FROM
(
SELECT MAX(tbl.effectivedate) OVER (PARTITION BY tbl.client, tbl.fund) AS MaxEffDateByClientByFund,
tbl.effectivedate,
tbl.client,
tbl.fund,
tbl.rundate,
tbl.userid
FROM Table1 tbl
WHERE effectivedate < = '20141101'
) tbl2
WHERE effectivedate = MaxEffDateByClientByFund;

Related

Group by a column and build a single result row based on condition

I need to group rows by account.
If there's only one row in a group, select it.
If there are multiple rows per group, select columns of the row with order_number equal to 4 but set order_number to 1.
myTable data:
account order_number status state
1111 4 ok full
2256 4 ok full
3344 1 NULL NULL
1111 1 NULL NULL
8743 4 ok full
2256 1 NULL NULL
Here's what I've tried:
select
account,
order_number,
status,
state,
case
when order_number = '1' then 'pass'
when order_number = '4' then 'fail'
end as ' TEST RESULTS '
from myTable
This is the result I'm trying to achieve:
account order_number status state
1111 1 ok full
2256 1 ok full
3344 1 NULL NULL
8743 4 ok full
here is simplest way & probably most performant solution:
select accounts
,case when cnt > 1 then 1 else order_number end order_number
,status,state
from (
select *
, row_number() over (partition by account order by case when order_number = 4 then 1 else 0 end desc) rn
, count(*) over (partition by account) cnt
) t
where rn = 1
You can do it this way:
Create dummy table for testing
[Create table test1(accounts varchar(10), order_number int, status varchar(10),state varchar(10))
insert into test1 values('1111',4,'ok','full')
insert into test1 values( '2256' , 4 , 'ok' , 'full')
insert into test1 values('3344' , 1 , NULL , NULL)
insert into test1 values('1111' , 1 , NULL , NULL)
insert into test1 values('8743' , 4 , 'ok' , 'full')
insert into test1 values('2256' , 1 , NULL , NULL)][1]
Query, no hard coded values
Select accounts,
order_number,
status,
state
from (
select row_number() over(partition by t1.accounts order by t1.order_number desc) rnum,
t1.accounts,
isnull(t2.order_number,t1.order_number) order_number ,
t1.status,
t1.state
from test1 t1
left join (select * from test1 where order_number=1) t2 on t1.accounts = t2.accounts and t1.order_number <> t2.order_number
) a
where rnum = 1
Result set
accounts order_number status state
---------- ------------ ---------- ----------
1111 1 ok full
2256 1 ok full
3344 1 NULL NULL
8743 4 ok full
UPDATE: Adding Test Result Column
Select accounts,
order_number,
status,
state,
[TEST RESULTS]
from (
select row_number() over(partition by t1.accounts order by t1.order_number desc) rnum,
t1.accounts,
isnull(t2.order_number,t1.order_number) order_number ,
t1.status,
t1.state,
case
when isnull(t2.order_number,t1.order_number) = '1' then 'pass'
when isnull(t2.order_number,t1.order_number) = '4' then 'fail'
end as 'TEST RESULTS'
from test1 t1
left join (select * from test1 where order_number=1) t2 on t1.accounts = t2.accounts and t1.order_number <> t2.order_number
) a
where rnum = 1
Just another option using WITH TIES in concert with the window functions min() over() and row_number() over()
Example
Select top 1 with ties
account
,order_number = min(order_number) over(partition by account)
,status
,state
From myTable
Order By row_number() over (partition by account order by order_number desc)
Results
account order_number status state
1111 1 ok full
2256 1 ok full
3344 1 NULL NULL
8743 4 ok full
I only have Access to work with. Output accomplished with:
Query1:
SELECT Q1.account, Q1.order_number, Q2.status, Q2.state
FROM (SELECT DISTINCT account, order_number FROM myTable WHERE order_number = 1) AS Q1
INNER JOIN (SELECT DISTINCT account, status, state FROM myTable WHERE order_number=4) AS Q2
ON Q1.account = q2.account;
Query2:
SELECT account, order_number, status, state FROM Query1
UNION SELECT account, order_number, status, state FROM myTable WHERE NOT account IN(SELECT account FROM Query1);
This query has the desired result, but I have doubts about the WHERE NOT EXISTS part, because I don't know what is the meaning of order_number and, if your real problem is more complex than your question, it may become complicated.
Just changed ok to 1 and full to 1
SELECT [T1].[account], [T1].[order_number], COALESCE([T2].[status], [T1].[status]) AS [status], COALESCE([T2].[state], [T1].[state]) AS [state]
FROM [dbo].[myTable] [T1]
LEFT JOIN [dbo].[myTable] [T2]
ON [T2].[account] = [T1].[account]
AND [T2].[order_number] = 4
WHERE NOT EXISTS (
SELECT 1
FROM [dbo].[myTable] [T3]
WHERE [T3].[account] = [T1].[account]
AND [T1].[order_number] = 4
AND [T3].[order_number] = 1
);
If order_number is always 1 and 4 then below could be the most optimized solution for your problem. Here first I have put a account wise sequence number for all rows starting from 1 in descending order of order_number. So If there is one row for any account number then it 's sequence number (rn) will be 1 and if there are more than one rows then row with order_number 4 will have the sequence number (rn) 1.
So we got our row to select. And to replace order_number 4 with 1 if it's not the only row we calculated row count for each account as column cnt. If cnt>1 and order_number is 4 then we replaced order_number with 1 using case when.
Schema and insert statements:
create table myTable(account int, order_number int, status varchar(10), state varchar(10));
insert into myTable values(1111, 4, 'ok', 'full');
insert into myTable values(2256, 4, 'ok', 'full');
insert into myTable values(3344, 1, NULL, NULL);
insert into myTable values(1111, 1, NULL, NULL);
insert into myTable values(8743, 4, 'ok', 'full');
insert into myTable values(2256, 1, NULL, NULL);
Query:
with cte as
(
select account,order_number,status,state,row_number()over(partition by account order by order_number desc)rn,
count(order_number)over(partition by account )cnt
from mytable
)select account,(case when order_number=4 and cnt>1 then 1 else order_number end) order_number,status,state
from cte where rn=1
Output:
account
order_number
status
state
1111
1
ok
full
2256
1
ok
full
3344
1
null
null
8743
4
ok
full
db<>fiddle here
You can achieve the above result by using Aggregate Function Like MIN & MAX
DECLARE #myTable TABLE (account int, order_number int, status varchar(10), state varchar(10))
INSERT INTO #myTable VALUES(1111, 4, 'ok', 'full');
INSERT INTO #myTable VALUES(2256, 4, 'ok', 'full');
INSERT INTO #myTable VALUES(3344, 1, NULL, NULL);
INSERT INTO #myTable VALUES(1111, 1, NULL, NULL);
INSERT INTO #myTable VALUES(8743, 4, 'ok', 'full');
INSERT INTO #myTable VALUES(2256, 1, NULL, NULL);
Query:
SELECT account,MIN(order_number) order_number,MAX(status) status,MAX(State) State
FROM #myTable
GROUP BY account

Find the most recent record based on a specific value from the same table

I have a MyTable with this values (columns Id and MyDate)
10 2019-01-01
10 2018-01-01
25 2020-01-01
25 2005-01-01
I'd like keep record based on the most recent date, the result should be
10 2019-01-01
25 2020-01-01
Do you have an idea ?
Thanks,
You can try this using ROW_NUMBER (Transact-SQL)
Create table MyTable (Id int, DtDate Date)
insert into MyTable Values
(10, '2019-01-01'),
(10, '2018-01-01'),
(25, '2020-01-01'),
(25, '2005-01-01')
select * from (
select id
, dtDate
, ROW_NUMBER() OVER(Partition By Id ORDER BY DtDate DESC) AS RowNo
from MyTable
)a where RowNo = 1
Live db<>fiddle demo.
Try this:
select t.* from (
select *,row_number() over (partition by ID order by date desc) as RN from Table ) t
where rn=1
select id, max(myDate)
from myTable
group by id
Try this:
select Id , MyDate
from (select m.*,
row_number() over (partition by Id order by MyDate desc) as rowNum
from MyTable m
) t
where rowNum = 1
Often, the faster method (with the right indexing) is:
select t.*
from t
where t.mydate = (select max(t2.mydate) as t t2 where t2.id = t.id);
The best index is on (id, mydate).
Note: For a small amount of data, the approach doesn't make much difference.

get records that have only 1 record per group

we have attendance db data as follows (sql server)
empid date type
1 01-Jan In
1 01-Jan Out
2 01-Jan In
3 01-Jan In
3 01-Jan Out
How can we get records that have only 1 record per date per employee (in above case empid 2 for 01-jan)?
The query should simply list all records of employees that have only single type for a day.
EDIT
The result set should be a bit more specific: show all employee who only have "In" for a date but no "Out"
Use Having
select empid, date, count(*)
from Mytable
group by empid, date
having count(*) = 1
You can use this to get the full line:
select t1.*
from MyTable t1
inner join
(
select empid, date, count(*)
from Mytable
group by empid, date
having count(*) = 1
) t2
on t1.empid = t2.empid
and t1.date = t2.date
You can use window functions:
select t.*
from (select t.*,
count(*) over (partition by empid, date) as cnt
from t
) t
where cnt = 1;
You can also use aggregation:
select empid, date, max(type) as type
from t
group by empid, date
having count(*) = 1;
Use a correlated subquery
select * from tablename a
where not exists (select 1 from tablename b where a.empid=b.empid and a.date=b.date and type='Out')
OR
select empid, date,count(distinct type)
from tablename
group by empid,date
having count(distinct type)=1
The Solution is Very Simple, You can use 'DISTINCT' function.
Query Should be as,
SELECT DISTINCT empid FROM attendance
This will return only 1 record per date per employee.
For Your Reference, Check it out- https://www.techonthenet.com/sql_server/distinct.php
This will work if we have ID with 1 IN OR 1 OUT as well
Declare #t table (empid int,date varchar(50),types varchar(50))
insert into #t values (1,'01-Jan','IN')
insert into #t values (1,'01-Jan','OUT')
insert into #t values (2,'01-Jan','IN')
insert into #t values (3,'01-Jan','OUT')
insert into #t values (4,'01-Jan','OUT')
select * from #t a
where not exists (select 1 from #t b where a.empid=b.empid and a.types!=b.types)

Find most recent record by date

This is my original data (anonymised):
id usage verified date
1 4000 Y 2015-03-20
2 5000 N 2015-06-20
3 6000 N 2015-07-20
4 7000 Y 2016-09-20
Original query:
SELECT
me.usage,
mes.verified,
mes.date
FROM
Table1 me,
Table2 mes,
Table3 m,
Table4 mp
WHERE
me.theFk=mes.id
AND mes.theFk=m.id
AND m.theFk=mp.id
How would I go about selecting the most recent verified and non-verified?
So I would be left with:
id usage verified date
1 6000 N 2015-07-20
2 7000 Y 2016-09-20
I am using Microsoft SQL Server 2012.
First, do not use implicit joins. This was discontinued more than 10 years ago.
Second, embrace the power of the CTE, the in clause and row_number:
with CTE as
(
select
me.usage,
mes.verified,
mes.date,
row_number() over (partition by Verified order by Date desc) as CTEOrd
from Table1 me
inner join Table2 mes
on me.theFK = mes.id
where mes.theFK in
(
select m.id
from Table3 m
inner join Table4 mp
on mp.id = m.theFK
)
)
select CTE.*
from CTE
where CTEOrd = 1
You can select the TOP 1 ordered by date for verified=N, union'd with the TOP 1 ordered by date for verified=Y.
Or in pseudo SQL:
SELECT TOP 1 ...fields ...
FROM ...tables/joins...
WHERE Verified = 'N'
ORDER BY Date DESC
UNION
SELECT TOP 1 ...fields ...
FROM ...tables/joins...
WHERE Verified = 'Y'
ORDER BY Date DESC
drop table #stack2
CREATE TABLE #stack2
([id] int, [usage] int, [verified] varchar(1), [date] datetime)
;
INSERT INTO #stack2
([id], [usage], [verified], [date])
VALUES
(1, 4000, 'Y', '2015-03-20 00:00:00'),
(2, 5000, 'N', '2015-06-20 00:00:00'),
(3, 6000, 'N', '2015-07-20 00:00:00'),
(4, 7000, 'Y', '2016-09-20 00:00:00')
;
;with cte as (select verified,max(date) d from #stack2 group by verified)
select row_number() over( order by s2.[verified]),s2.[usage], s2.[verified], s2.[date] from #stack2 s2 join cte c on c.verified=s2.verified and c.d=s2.date
As per the data shown i had written the query.
for your scenario this will be use full
WITH cte1
AS (SELECT me.usage,
mes.verified,
mes.date
FROM Table1 me,
Table2 mes,
Table3 m,
Table4 mp
WHERE me.theFk = mes.id
AND mes.theFk = m.id
AND m.theFk = mp.id),
cte
AS (SELECT verified,
Max(date) d
FROM cte1
GROUP BY verified)
SELECT Row_number()
OVER(
ORDER BY s2.[verified]),
s2.[usage],
s2.[verified],
s2.[date]
FROM cte1 s2
JOIN cte c
ON c.verified = s2.verified
AND c.d = s2.date
You can as the below Without join.
-- Mock data
DECLARE #Tbl TABLE (id INT, usage INT, verified CHAR(1), date DATETIME)
INSERT INTO #Tbl
VALUES
(1, 4000 ,'Y', '2015-03-20'),
(2, 5000 ,'N', '2015-06-20'),
(3, 6000 ,'N', '2015-07-20'),
(4, 7000 ,'Y', '2016-09-20')
SELECT
A.id ,
A.usage ,
A.verified ,
A.MaxDate
FROM
(
SELECT
id ,
usage ,
verified ,
date,
MAX(date) OVER (PARTITION BY verified) MaxDate
FROM
#Tbl
) A
WHERE
A.date = A.MaxDate
Result:
id usage verified MaxDate
----------- ----------- -------- ----------
3 6000 N 2015-07-20
4 7000 Y 2016-09-20
CREATE TABLE #Table ( ID INT ,usage INT, verified VARCHAR(10), _date DATE)
INSERT INTO #Table ( ID , usage , verified , _date)
SELECT 1,4000 , 'Y','2015-03-20' UNION ALL
SELECT 2, 5000 , 'N' ,'2015-06-20' UNION ALL
SELECT 3, 6000 , 'N' ,'2015-07-20' UNION ALL
SELECT 4, 7000 , 'Y' ,'2016-09-20'
SELECT ROW_NUMBER() OVER(ORDER BY usage) ID,usage , A.verified , A._date
FROM #Table
JOIN
(
SELECT verified , MAX(_date) _date
FROM #Table
GROUP BY verified
) A ON #Table._date = A._date

Group by id and select most recent

I have a table example like this:
date id status
01/01/2013 55555 high
01/01/2014 55555 low
01/01/2010 44444 high
01/01/2011 33333 low
I need in order: group by id and select most recent date.
this is the result I want.
date id status
01/01/2014 55555 low
01/01/2010 44444 high
01/01/2011 33333 low
I do not care the order of the rows.
you need to join your table with a subquery that "links" the record date with the greatest date for each id:
select a.*
from your_table as a
inner join (
select id, max(date) as max_date
from your_table
group by id
) as b on a.id = b.id and a.date = b.max_date;
I think you will need a subquery to get the MAX(Date) and then inner join. Try this:
SELECT A.[Date], A.[Id], A.[Status]
FROM Table A
INNER JOIN(SELECT Id, MAX([Date]) AS MaxDate
FROM Table
GROUP BY [Id]) B ON
A.[Id] = B.[Id] AND
A.[Date] = B.[MaxDate]
--return the group id and the latest date in that group
select id
, MAX([date]) [latestDateInGroup]
from tbl
group by id
--return the group id, and the related status and date for the record with the latest date in that group
select id
, [status] [latestDateInGroup'sStatus]
, [date] [latestDateInGroup]
from
(
select id
, [status]
, [date]
, row_number() over (partition by id order by [date] desc) r
from tbl
) x
where x.r = 1
--return all ids and statuses, along with the latest date in that group's group (requires SQL 2012+)
select id
, [status]
, max([date]) over (partition by id order by [date] desc) [latestDateInGroup]
from tbl
SQL Fiddle's offline at the moment; once back up the following code should allow you to build a table to test the above queries with
http://sqlfiddle.com
create table tbl ([date] date, id bigint, [status] nvarchar(4))
go
insert tbl select '2013-01-01', 55555, 'high'
insert tbl select '2014-01-01', 55555, 'low'
insert tbl select '2010-01-01', 44444, 'high'
insert tbl select '2011-01-01', 33333, 'low'