SQL Server: aggregate to single result - sql

I have this query
SELECT Client.ClientNo,
Client.ContactName,
Deal.Currency,
MAX(Deal.DealDate)
FROM Deal
JOIN Client ON Deal.ClientNo = Client.ClientNo
GROUP BY Client.ClientNo, Client.ContactName, Deal.Currency;
which gives me a result
1 John Smith EUR 2014-10-07
1 John Smith GBP 2014-11-12
2 Jane Doe GBP 2014-09-17
2 Jane Doe USD 2014-12-23
1 John Smith USD 2013-11-13
2 Jane Doe EUR 2012-09-06
Problem is, I need an aggregated result with the latest date per client, like this:
1 John Smith GBP 2014-11-12
2 Jane Doe USD 2014-12-23
How can I change my query to achieve this?
UPDATE Thanks to jarlh for the answer, however I have missed something - if there is a duplicate row - it will remain in the result, looking like this:
1 John Smith GBP 2014-11-12
1 John Smith GBP 2014-11-12
2 Jane Doe USD 2014-12-23
Any way to make that work?

You could do something like this:
Test data:
DECLARE #Deal TABLE(ClientNo INT,Currency VARCHAR(10),DealDate DATETIME)
DECLARE #Client TABLE(ClientNo INT,ContactName VARCHAR(100))
INSERT INTO #Deal
VALUES (1,'EUR','2014-10-07'),(1,'GBP','2014-11-12'),(2,'GBP','2014-09-17'),
(2,'USD','2014-12-23'),(1,'USD','2013-11-13'),(2,'EUR','2012-09-06')
INSERT INTO #Client
VALUES (1,'John Smith'),(2,'Jane Doe')
Query:
;WITH latestDeals
AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY ClientNo ORDER BY DealDate DESC) AS RowNbr,
Deal.*
FROM
#Deal AS Deal
)
SELECT
client.ClientNo,
client.ContactName,
latestDeals.Currency,
latestDeals.DealDate
FROM
#Client AS client
JOIN latestDeals
ON client.ClientNo=latestDeals.ClientNo
AND latestDeals.RowNbr=1
Update:
If you want to use conventional query. You could do something like this:
SELECT
client.ClientNo,
client.ContactName,
Latestdeal.maxDealDate as DealDate,
deal.Currency
FROM
#Client AS client
JOIN
(
SELECT
MAX(Deal.DealDate) AS maxDealDate,
Deal.ClientNo
FROM
#Deal AS Deal
GROUP BY
Deal.ClientNo
) AS Latestdeal
ON client.ClientNo=Latestdeal.ClientNo
JOIN #Deal as deal
ON client.ClientNo=deal.ClientNo
AND deal.DealDate=Latestdeal.maxDealDate
This will result in the same output
Result:
1 John Smith GBP 2014-11-12 00:00:00.000
2 Jane Doe USD 2014-12-23 00:00:00.000

Untested, but should work. Will return several rows for a clieant if the client has two (or more) deals the same, latest day.
SELECT Client.ClientNo,
Client.ContactName,
Deal.Currency,
Deal.DealDate
FROM Deal
JOIN Client ON Deal.ClientNo = Client.ClientNo
WHERE Deal.DealDate = (select max(DealDate) from Deal
where ClientNo = Client.ClientNo)

Try this,
Test Data:
CREATE TABLE #YourTable
(
CLIENT_NO INT,
CONTACT_NAME VARCHAR(20),
CURRENCY VARCHAR(10),
[DEAL_DATE] DATE
)
INSERT INTO #YourTable VALUES
(1,'John Smith','EUR','2014-10-07'),
(1,'John Smith','GBP','2014-11-12'),
(2,'Jane Doe','GBP','2014-09-17'),
(2,'Jane Doe','USD','2014-12-23'),
(1,'John Smith','USD','2013-11-13'),
(2,'Jane Doe','EUR','2012-09-06')
Query:
SELECT CLIENT_NO,CONTACT_NAME,CURRENCY,[DEAL_DATE]
FROM (SELECT *,
Row_Number()
OVER (
PARTITION BY CLIENT_NO
ORDER BY [DEAL_DATE] DESC) AS RN
FROM #YourTable)A
WHERE RN = 1

Related

How do I select a max date by person in a table

I am not too advanced with SSRS/SQL queries, and need to write a report that pulls out % allocations by person to then compare to a wage table to allocate the wages. These allocations change quarterly, but all allocations continue to be stored in the table. If a persons allocation did not change, they do NOT get a new entry in the table. Here is a sample table called Allocations.
First Name
Last Name
Date
Area
Percent
Smith
Bob
01/01/20
A
50.00
Smith
Bob
01/01/20
B
50.00
Doe
Jane
01/01/20
A
25.00
Doe
Jane
01/01/20
B
25.00
Doe
Jane
01/01/20
C
50.00
Doe
Jane
04/01/20
A
35.00
Doe
Jane
04/01/20
C
65.00
Wayne
Bruce
01/01/20
A
100.00
Wayne
Bruce
04/01/20
B
100.00
The results that I would want to have from this sample table when querying it are:
First Name
Last Name
Date
Area
Percent
Smith
Bob
01/01/20
A
50.00
Smith
Bob
01/01/20
B
50.00
Doe
Jane
04/01/20
A
35.00
Doe
Jane
04/01/20
C
65.00
Wayne
Bruce
04/01/20
B
100.00
However, I would also like to pull this by comparing it to a date that the user inputs, so that they could run this report at any point in time and get the correct "max" dates. So, for example, if there were also 7/1/20 dates in here, but the user input date was 6/30/20, I would NOT want to pull the 7/1/20 data. In other words, I would like to pull the rows with the maximum date by name w/o going over the user's input date.
Any idea on the best way to accomplish this?
Thanks in advance for any advice you can provide.
In SQL, ROW_NUMBER can be used to order records in groups by a particular field.
SELECT * FROM (
SELECT *, ROW_NUMBER()OVER(PARTITION BY Last_Name, First_Name ORDER BY DATE DESC) as ROW_NUM
FROM TABLE
) AS T
WHERE ROW_NUM = 1
Then you filter for ROW_NUM = 1.
However, I noticed that there are a couple with the same date and you want both. In this caseyou'd want to use RANK - which allows for ties so there may be multiple records with the same date that you want to capture.
SELECT * FROM (
SELECT *, RANK()OVER(PARTITION BY Last_Name, First_Name ORDER BY DATE DESC) as ROW_NUM
FROM TABLE
) AS T
WHERE ROW_NUM = 1

SQL Query: New columns for data with some matching criteria

I'm working with a query that pulls data from a table and arranges it in a manner similar to below:
Query1
BldID UnitID Res1
1 201 John Smith
1 201 Jane Doe
1 202 Daniel Jones
1 202 Mark Garcia
2 201 Maria Lee
2 201 Paul Williams
2 201 Mike Jones
I'd like to modify the query output in SQL/Design so that each resident that shares a building / unit shows as a new column on the same row as shown below:
BldID UnitID Res1 Res2 Res3
1 201 John Smith Jane Doe
1 202 Daniel Jones Mark Garcia
2 201 Maria Lee Paul Williams Mike Jones
I apologize if this is crude/not enough information but any help would be greatly appreciated.
You can try using conditional aggregation
with cte as
(
select *, row_number() over(partition by BldID,UnitID order by Res1) as rn
from tablename
)
select BldID,UnitID,
max(case when rn=1 then Res1 end) as Res1,
max(case when rn=2 then Res1 end) as Res2,
max(case when rn=3 then Res1 end) as Res3
from cte
group by BldID,UnitID
So, drawing from a few different sources, this might work, try pasting this intoa query editor, and see if it'll run.
TRANSFORM MAX(Res1)
SELECT BldID, UnitID
, (
SELECT COUNT(T1.Marks)
FROM tableName AS T1
WHERE
T1.BldgID = T2.BldgID AND
T1.UnitID = T2.UnitID AND
T1.Res1 >= T2.Res1
) AS Rank, Res1
FROM tableName t2
GROUP BY BldID, UnitID
PIVOT Rank;
2 years late, but maybe I can add something, in Access we are surgeons operating with kitchen knifes, things must be done in the Access Way...
I tested it having this table UnitStudentBlock
BldID
UnitID
Res1
1
201
John Smith
1
201
Jane Doe
1
202
Daniel Jones
1
202
Mark Garcia
2
201
Maria Lee
2
201
Paul Williams
2
201
Mike Jones
2
201
Julian Gomes
As Access doesn't have row_number, first I created a table with an auto increment field so that we can have something like a row number:
CREATE TABLE TableWithId
(
Id COUNTER,
BldID INT,
UnitID INT,
Res1 VARCHAR(100),
ResNumber VARCHAR(100)
)
Then I inserted all the data from the initial table into this newly created table:
INSERT INTO TableWithId (BldID, UnitID, Res1)
SELECT *
FROM UnitStudentBlock
ORDER BY BldID,
UnitID
Then I updated everything using DCOUNT to have a row_number partitioned:
UPDATE TableWithId
SET ResNumber = 'Res' + Cstr(DCOUNT("*", "TableWithId", "ID >=" & [ID]
& " AND UnitId = " & [UnitId]
& " AND BldId = " & [BldId]))
And finally we can run the query that returns the data:
TRANSFORM MAX(Res1)
SELECT BldID, UnitID
FROM TableWithId
GROUP BY BldID, UnitID
PIVOT ResNumber

Renumbering rows in SQL Server

I'm kinda new into the SQL Server and I'm having the following question: is there any possibility to renumber the rows in a column?
For ex:
id date name
1 2016-01-02 John
2 2016-01-02 Jack
3 2016-01-02 John
4 2016-01-02 John
5 2016-01-03 Jack
6 2016-01-03 Jack
7 2016-01-04 John
8 2016-01-03 Jack
9 2016-01-02 John
10 2016-01-04 Jack
I would like that all "Johns" to start with id 1 and go on (2, 3, 4 etc) and all "Jacks" have the following number when "John" is done (5, 6, 7 etc). Thanks!
I hope this helps..
declare #t table (id int ,[date] date,name varchar(20))
insert into #t
( id, date, name )
values (1,'2016-01-02','John')
,(2,'2016-01-02','Jack')
,(3,'2016-01-02','John')
,(4,'2016-01-02','John')
,(5,'2016-01-03','Jack')
,(6,'2016-01-03','Jack')
,(7,'2016-01-04','John')
,(8,'2016-01-03','Jack')
,(9,'2016-01-02','John')
,(10,'2016-01-04','Jack')
select
row_number() over(order by name,[date]) as ID,
date ,
name
from
#t
order by name
The id should just be an internal identifier you use for joins etc - I wouldn't change it. But you could query such a numbering using a window function:
SELECT ROW_NUMBER() OVER (ORDER BY CASE name WHEN 'John' THE 1 ELSE 2 END) AS rn,
date,
name
FROM mytable
Instead of renumbering the id column, you can use ROW_NUMBER window function to renumber the rows as per your requirement. for e.g.:
SELECT ROW_NUMBER() OVER(PARTITION BY name ORDER BY date) as rowid,date,name
FROM tablename

sql that identifies which account numbers have multiple agents

I dont think a count will work here, can someone help me get an sql that identifies which account numbers have multiple agents, more than two agents in the where condition.
AGENT_NAME ACCOUNT_NUMBER
Clemons, Tony 123
Cipollo, Michael 123
Jepsen, Sarah 567
Joanos, James 567
McMahon, Brian 890
Novak, Jason 437
Ralph, Melissa 197
Reitwiesner, John 221
Roman, Marlo 123
Rosenzweig, Marcie 890
Results should be something like this.
ACCOUNT_NUMBER AGENT_NAME
123 Cipollo, Michael
123 Roman, Marlo
123 Clemons, Tony
890 Rosenzweig, Marcie
890 McMahon, Brian
567 Joanos, James
567 Jepsen, Sarah
You can do this using window functions:
select t.account_number, t.agent_name
from (select t.*, min(agent_name) over (partition by account_number) as minan,
max(agent_name) over (partition by account_number) as maxan
from table t
) t
where minan <> maxan;
If you know the agent names are never duplicated, you could just do:
select t.account_number, t.agent_name
from (select t.*, count(*) over (partition by account_number) as cnt
from table t
) t
where cnt > 1;
Assuming your table name is test, this should pull all the records with duplicate ACCOUNT_NUMBER:
select * from test where ACCOUNT_NUMBER in
(select ACCOUNT_NUMBER from test
group by ACCOUNT_NUMBER having
count(ACCOUNT_NUMBER)>1)
order by ACCOUNT_NUMBER
Using count function u can get the result
CREATE TABLE #TEMP
(
AGENT_NAME VARCHAR(100),
ACCOUNT_NUMBER INT
)
INSERT INTO #TEMP
VALUES ('CLEMONS, TONY',123),
('CIPOLLO, MICHAEL',123),
('JEPSEN, SARAH',567),
('JOANOS, JAMES',567),
('MCMAHON, BRIAN',890),
('NOVAK, JASON',437),
('RALPH, MELISSA',197),
('REITWIESNER, JOHN',221),
('ROMAN, MARLO',123),
('ROSENZWEIG, MARCIE',890)
SELECT a.ACCOUNT_NUMBER,a.AGENT_NAME
FROM #TEMP A
JOIN(SELECT COUNT(1) CNT,
ACCOUNT_NUMBER
FROM #TEMP
GROUP BY ACCOUNT_NUMBER) B
ON A.ACCOUNT_NUMBER = B.ACCOUNT_NUMBER
WHERE B.CNT != 1

SQL Query to return the difference between records of two most recent dates

I have the following table:
**TABLE1**
RecordID UserID UserName Balance TranDate
---------------------------------------------------------------
100 10001 John Doe 10213.00 2013-02-12 00:00:00.000
101 10001 John Doe 1932.00 2013-04-30 00:00:00.000
102 10001 John Doe 10213.00 2013-03-25 00:00:00.000
103 10001 John Doe 14514.00 2013-04-12 00:00:00.000
104 10001 John Doe 5430.00 2013-02-19 00:00:00.000
105 10001 John Doe 21242.00 2010-02-11 00:00:00.000
106 10001 John Doe 13342.00 2013-05-22 00:00:00.000
Now what i'm trying to do is to query the two most recent transactions and arrive at this data:
RecordID UserID UserName Balance TranDate
---------------------------------------------------------------
106 10001 John Doe 13342.00 2013-05-22 00:00:00.000
101 10001 John Doe 1932.00 2013-04-30 00:00:00.000
Then using the data above I would like to compare the balances to show the difference:
UserID UserName Difference
---------------------------------------------------------------
10001 John Doe -11410.00
This just shows the difference between the two previous balances (the latest and the balance before the latest)
Now I have the following query below. This works okay to show the two most recent transactions.
SELECT
TOP 2 *
FROM Table1
WHERE UserID = '1001'
ORDER
BY TranDate DESC
Now my issues are:
Is the sql above safe to use? I am just relying on the sorting of the TranDate by the ORDER BY DESC keyword and I am not so sure if this is very much reliable or not.
How do I select the difference between the two Balances (Row 2 - Row 1 )? I was looking for some answers online and I find stuff about self-joining. I tried it but it doesn't show me my desired output.
EDIT:
This is the closest I can get to my desired result. Can someone help me out on this please? Thanks!
DECLARE #SampleTable TABLE
(
UserID INT,
UserName VARCHAR(20),
Balance DECIMAL(9,2) DEFAULT 0
)
INSERT
INTO #SampleTable
(UserID, UserName, Balance)
SELECT
TOP 2 UserID,
UserName,
Balance
FROM Table1
WHERE UserID = '1001'
ORDER
BY TranDate DESC
SELECT A.UserID,
A.UserName,
B.Balance - A.Balance AS Difference
FROM #SampleTable A
JOIN #SampleTable B
ON A.UserID = B.UserID
Thanks a lot!
You should be able to use something like the following assuming SQL Server as the RDBMS:
;with cte as
(
select recordid, userid, username, balance, trandate,
row_number() over(partition by userid order by trandate desc) rn
from table1
)
select c1.userid, c1.username,
c1.balance - c2.balance diff
from cte c1
cross apply cte c2
where c1.rn = 1
and c2.rn = 2;
See SQL Fiddle with demo.
Or this could be done using an INNER JOIN on the row_number value:
;with cte as
(
select recordid, userid, username, balance, trandate,
row_number() over(partition by userid order by trandate desc) rn
from table1
)
select c1.userid, c1.username,
c1.balance - c2.balance diff
from cte c1
inner join cte c2
on c1.rn + 1 = c2.rn
where c1.rn = 1
See SQL Fiddle with Demo