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

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

Related

Build time window counters from raw data - Big Query

Consider raw events data regarding purchases in 2020, as per the following table:
BUYER DATE ITEM
Joe '2020-01-15' Dr. Pepper
Joe '2020-02-15' Dr. Pepper
Joe '2020-03-15' Dr. Pepper
Joe '2020-05-15' Dr. Pepper
Joe '2020-10-15' Dr. Pepper
Joe '2020-12-15' Dr. Pepper
I would like to aggregate the data to see what Joe did in a monthly moving sum, i.e., obtaining as an outcome
BUYER Date Num_Purchases_last_3months
Joe '2020-01-31' 1
Joe '2020-02-31' 2
Joe '2020-03-31' 3
Joe '2020-04-31' 2
.
.
.
Joe '2020-11-31' 1
Joe '2020-12-31' 2
How could I obtain the desired result in an efficient query?
You can use window functions, in this case, count(*) with a range window frame specification:
select t.*,
count(*) over (partition by buyer
order by extract(year from date) * 12 + extract(month from date)
range between 2 preceding and current row
) as Num_Purchases_last_3months
from t;

SQL - Joining Two Tables and Sum of Column

I have a situation where I have table A that have member info and table B that has a list of transactions for the members. I need to retrieve fields from Table A with a total of the transaction column for each member.
I have struggled with the proper SQL syntax and it keeps error on me. We are using MS Reporting Services to develop this if that helps.
Table A:
Member ID LName FName Phone
----------------------------------------------
1234 Doe John 555-555-5555
5678 Doe Jane 555-555-5550
Table B:
Member ID Transaction Date Transaction Total
----------------------------------------------------
1234 01-01-2020 120.00
1234 01-05-2020 25.00
5678 01-01-2020 50.00
5678 01-10-2020 50.00
5678 01-11-2020 25.00
1234 01-15-2020 25.00
Desired output:
Member ID: Last Name: First Name: Total:
----------------------------------------------------
1234 Doe John 170.00
5678 Doe Jane 125.00
You are looking for aggregation with group by & sum() :
select a.memberid, a.lname, a.fname, sum(b.transactiontotal) as total
from a inner join
b
on b.memberid = a.memberid
group by a.memberid, a.lname, a.fname;

SQL Server query to 'ftatten' data for reporting

Say I have a table with the following data, in the following structure. I'm trying to query the data to find the date ranges that someone (employee) worked.
NAME WORKED DATE
Bob YES 1/1/2019
Bob YES 1/2/2019
Bob YES 1/3/2019
Bob NO 1/4/2019
Bob YES 1/5/2019
Bob YES 1/6/2019
Bob NO 1/7/2019
Jane Yes 1/1/2019
Jane Yes 1/2/2019
Jame No 1/3/2019
Expected Result: (The Result I need)
Bob 1/1/2019 - 1/3/2019
Bob 1/5/2019 - 1/6/2019
Jane 1/1/2019 - 1/2/2019
What's the SQL syntax (SQL Server 2008+) of the query to return this result set?
thx in advance
This is a gaps-and-islands problem. You can identify the rows using row_number() and some date arithmetic.
So, assuming you have a row for every date:
select name, min(date), max(date)
from (select t.*,
row_number() over (partition by name order by date) as seqnum
from t
where worked = 'YES'
) t
group by name,
dateadd(day, - seqnum, date);
Why does this work? You are looking for adjacent dates. If you subtract a sequence from the dates, then the result is constant -- when the dates are sequential. This observation is used in the group by to get the groups you want.

Oracle SQL -- selecting specific data from multiple rows into one row

I'm trying to select data across multiple rows into one row.
For example, with this data set:
NAME THING DATE
----- ------ ------
JACK 1 EARLY
JACK 2 LATER
JACK 3 NOW
JANE 1 LATER
JANE 2 EARLY
JANE 3 NOW
I want to produce the following result:
NAME THING DATE
---- ---- -----
JACK 1, 2, 3 NOW
JANE 1, 2, 3 NOW
And so, I know i can use the LISTAGG function to combine the "Thing" rows, but my biggest question is how to select across multiple rows to get the "NOW" values in the date field.
Any help would be appreciated. Thanks!
It isn't clear if you want the latest date for any thing (ordering the aggregated things either by date or by their own values):
select name,
listagg(thing, ',') within group (order by date_col) as things,
max(date_col) as now
from your_table
group by name
order by name;
or the date corresponding to the highest value of thing:
select name,
listagg(thing, ',') within group (order by thing) as things,
max(date_col) keep (dense_rank last order by thing) as now
from your_table
group by name
order by name;
As you said those are actually dates, with your sample data with added date values configured slightly differently for two names:
NAME THING DATE_COL
---- ---------- ----------
JACK 1 2019-01-01
JACK 2 2019-03-15
JACK 3 2019-04-30
JANE 1 2019-02-01
JANE 2 2019-05-03
JANE 3 2019-04-02
the first query gets:
NAME THINGS NOW
---- --------------- ----------
JACK 1,2,3 2019-04-30
JANE 1,3,2 2019-05-03
and the second query gets:
NAME THINGS NOW
---- --------------- ----------
JACK 1,2,3 2019-04-30
JANE 1,2,3 2019-04-02
db<>fiddle

SQL - Spread a value across multiple weeks

I currently have data stored as follows:
PERSON DATE RATE
----------------------------
John Smith 1/4/2012 1.2
John Smith 8/6/2012 1.7
John Smith 8/13/2012 1.9
John Smith 8/20/2012 2
John Smith 9/10/2012 1.8
John Smith 10/1/2012 3
I'm trying to output a rate for each week (ending Sunday) of the year for each person. Where the rate doesn't exist for a given week, the previous week is used i.e.:
PERSON WEEK RATE
----------------------------
John Smith 1/8/2012 1.2
John Smith 1/15/2012 1.2
John Smith 1/22/2012 1.2
John Smith 1/29/2012 1.2
etc
I can build a table of for date and week combinations so I can determine the week. How can I duplicate the rate though? An outer join or something similar?
There's a lot I don't know about your data, but here's a suggestion. The first subquery selects all distinct persons, the second matches each of these to each week (supposing you store weeks as date and 00:00 am Sunday morning). This supposes that Person is unique, but I hope you have a primary key that can be used to determine who's who.
SELECT
X.Person,
W.Date,
X.Rate
FROM
(
SELECT
Person
FROM
Ratings
GROUP BY
Person
) P
CROSS JOIN
(
SELECT
Date AS DateEnd
FROM
Weeks
) W
CROSS APPLY
( -- Last Rate before week end
SELECT TOP 1
Rate
FROM
Ratings
WHERE
Person = P.Person AND
Date < Dateadd(DAY,1,W.DateEnd)
ORDER BY
DATE DESC
) X