SSRS - Grouping multiple rows per page based on varchar column - sql-server

I'm trying to create an SSRS report that will display all timesheets per person, per accounting period (month) on a single page. However, I'm stuggling to work out how the result should be grouped. So far all I seem to be able to do is get one line to appear per specified user per page.
My T-SQL looks like this:
SELECT tblADSI.DisplayName AS FullName,
ds.[Date of Work],
ds.Hours,
ds.[Description of Work],
ds.Mileage,
ds.Subsistence,
ds.[Activity Code] AS CompanyRef,
c.[Cost Center] AS DiaryClient,
r.Role,
c.Description AS Project,
ds.[Accounting Period],
ds.[Rechargeable Items]
FROM [Diary Sheets] ds
LEFT JOIN Commissions c on c.[Commission Code] = ds.[Commission Code]
LEFT JOIN dbo.Roles r on r.Commission = ds.[Commission Code] and r.EmployeeID = ds.EmployeeID
LEFT JOIN [Accounting Period] ap ON ap.ID = ds.[Accounting Period ID]
LEFT JOIN (SELECT displayName,
EmployeeID FROM OpenQuery (
ADSI,
'SELECT
displayName,
EmployeeID
FROM ''LDAP://example.domaincontroller.com/OU=Unit1,DC=domaincontroller,DC=com''
WHERE objectCategory = ''person''
AND objectClass = ''User''
') AS tblADSI
UNION
SELECT displayName,
EmployeeID
FROM OpenQuery (
ADSI,
'SELECT
displayName,
EmployeeID
FROM ''example.domaincontroller.com/OU=Unit2,DC=domaincontroller,DC=com''
WHERE objectCategory = ''person''
AND objectClass = ''User''
') AS tblADSI
) AS tblADSI
ON tblADSI.EmployeeID = ds.employeeID
WHERE c.[Commission Code] = 'Example Commission'
AND ap.[Accounting Period] = 'Febuary 2015'
ORDER BY FullName, ds.[Date of Work], ds.[Activity Code]
An example output from the SQL would be.
-- Name Date Of Work Hours
John Smith 01/02/2015 9
John Smith 02/02/2015 10
Jane Doe 01/02/2015 5
Jane Doe 02/02/2015 5
Mike Smith 01/02/2015 8
Mike Smith 02/02/2015 9
So using the SQL above as my example I've built a report which should display per page every user (based on the commission parameter set by the users) within a particular commission. However, all that seems to be happening in the report is it shows one row (the most recent entry for the February accounting period) and nothing more. For example:
-- Name Date Of Work Hours
John Smith 01/02/2015 9
Jane Doe 01/02/2015 5
Mike Smith 01/02/2015 8
I actually need it to show all diary sheets for the accounting period per user so in the report, using John Smith as the example, it would appear as so:
-- Name Date Of Work Hours
John Smith 01/02/2015 9
John Smith 02/02/2015 10
And so on and so forth for every other user on a new page.
Does anybody know how I can achieve this? I've looked into and tried some SSRS grouping (which I suspect is the solution) but I'm completely at a loss. No matter what type of grouping I try it always comes out the same.
I've been writing SQL for a couple of years, but I'm relatively new to SSRS and my SSRS knowledge completely self taught so some of my terminology/formatting is probably incorrect.

I managed to fix it by adding all columns from my SELECT statement as parent row groups on my tablix. Thanks mxix and NickyvV for your suggestions, much appreciated.

Related

Query Most Recent Records in MS Access Based on Date Provided in Form Field

Let me start by noting I have spent a few days searching through S.O. and have not been able to find a solution. I apologize in advance if the solution is very simple, but I am still learning and appreciate any help I can get.
I have a MS Access 2010 Database, and I am trying to create a set of queries to inform other forms and queries. There are two tables: Borrower Contact Info (BC_Info) and Basic Financial Indicators (BF_Indicators). Each month, I review and track key performance metrics of each borrower. I would like to create a query that supplies the most recent record based on a textbox input (Forms![Portfolio_Review Menu]!Text47).
Two considerations have separated this from other posts I have seen in the 'greatest-n-per-group' tag:
Not every borrower will have data for every month.
I need to be able to see back in time, i.e. if it is January 1, 2019 and I want to see the metrics as of July 31, 2017, I want to make
sure I am only seeing data from before July 31, 2017 but as close to
this date as possible.
Fields are as follows:
BC_Info
- BorrowerName
-PartnerID
BF_Indicators
-Fin_ID
-DateUpdated
The tables are connected by BorrowerName -- which is a unique naming convention used for the primary key of BC_Info.
What I currently have is:
SELECT BCI.BorrowerName, BCI.PartnerID, BFI.Fin_ID, BFI.DateUpdated
FROM ((BC_Info AS BCI
INNER JOIN BF_Indicators AS BFI
ON BFI.BorrowerName = BCI.BorrowerName)
INNER JOIN
(
SELECT Fin_ID, MAX(DateUpdated) AS MAX_DATE
FROM BF_Indicators
WHERE (DateUpdated <= Forms![Portfolio_Review Menu]!Text47 OR
Forms![Portfolio_Review Menu]!Text47 IS NULL)
GROUP BY Fin_ID
) AS Last_BF ON BFI.Fin_ID = Last_BF.Fin_ID AND
BFI.DateUpdated = Last_BF.MAX_DATE);
This gives me the fields I need, and will keep records out that are past the date given in the textbox, but will give all records from before the textbox input -- not just the most recent.
Results (Date Entered is 12/31/2018; MEHN-45543 is only Borrower with information later than 09/30/2018):
BorrowerName PartnerID Fin_ID DateUpdated
MEHN-45543 19 9 12/31/2018
ARYS-7940 5 10 9/30/2018
FINS-21032 12 11 9/30/2018
ELET-00934 9 12 9/30/2018
MEHN-45543 19 18 9/30/2018
Expected Results (Date Entered is 12/31/2018; MEHN-45543 is only Borrower with information later than 09/30/2018):
BorrowerName PartnerID Fin_ID DateUpdated
MEHN-45543 19 9 12/31/2018
ARYS-7940 5 10 9/30/2018
FINS-21032 12 11 9/30/2018
ELET-00934 9 12 9/30/2018
As mentioned, I am planning to use the results of this Query to generate further queries that use aggregated information from the Financial Indicators to determine portfolio quality at the time.
Please let me know if there is any other information I can provide. And again, thank you in advance.
Try joining BC_Info to a query that aggregates BF_Indicators on BorrowerName, not Fin_ID. Tested with literal date value:
SELECT BC_Info.*, MaxDate
FROM BC_Info
INNER JOIN
(SELECT BorrowerName, Max(DateUpdated) AS MaxDate
FROM BF_Indicators WHERE DateUpdated <=#12/31/2018# GROUP BY BorrowerName) AS Q1
ON BC_Info.BorrowerName=Q1.BorrowerName;
If you need to include Fin_ID in the results, then:
SELECT BC_Info.*, Fin_ID, DateUpdated FROM BC_Info
INNER JOIN
(SELECT * FROM BF_Indicators WHERE Fin_ID IN
(SELECT TOP 1 Fin_ID FROM BF_Indicators AS Dupe
WHERE Dupe.BorrowerName=BF_Indicators.BorrowerName AND DateUpdated<=#12/31/2018#
ORDER BY Dupe.DateUpdated DESC)
) AS Q1
ON BC_Info.BorrowerName = Q1.BorrowerName;
If you don't like TOP N, adjust your original query:
SELECT BCI.BorrowerName, BCI.PartnerID, BFI.Fin_ID, BFI.DateUpdated
FROM ((BC_Info AS BCI
INNER JOIN BF_Indicators AS BFI
ON BFI.BorrowerName = BCI.BorrowerName)
INNER JOIN
(
SELECT BorrowerName, MAX(DateUpdated) AS MAX_DATE
FROM BF_Indicators
WHERE (DateUpdated <= #12/31/2018#)
GROUP BY BorrowerName
) AS Last_BF ON BFI.BorrowerName = Last_BF.BorrowerName AND
BFI.DateUpdated = Last_BF.MAX_DATE);
And 1 more to think about:
SELECT BC_Info.PartnerID, BC_Info.BorrowerName, BF_Indicators.Fin_ID, BF_Indicators.DateUpdated
FROM BC_Info RIGHT JOIN BF_Indicators ON BC_Info.BorrowerName = BF_Indicators.BorrowerName
WHERE (((BF_Indicators.DateUpdated)=DMax("DateUpdated","BF_Indicators","BorrowerName='" & [BC_Info].[BorrowerName] & "' AND DateUpdated<=#12/31/2018#")));

SQL Join Tables on Closest Date BEFORE Shipped Date

This may appear to be a repeat question but it is not because the other solutions in the forum don't work in this situation.
This is a query to our ERP database that is trying to get the final cost of goods sold total for parts. Basically the ERP makes it easy to get all the direct costs out but doesn't calculate scrap costs.
Where I'm stuck is in the sub query in the FROM section marked:
>>>>>>HELP NEEDED STARTING HERE
The sub query, as it is now written, pulls out all the shipments to our scrap vendor and gets a monthly average rate per pound, then joins to the other tables based on alloy type, month and year.
My Finance Department has told me the average is not a good solution, since some metal prices fluctuate too much or they don't sell the scrap metal in the same month the parts shipped so this won't work.
I need to get the rate we are paid for scrap metal from the closest date before the part was shipped from our facility.
I've found other examples on Stack Overflow that show ways to do this but the main tables and the sub query tables overlap so the other solutions I've seen have failed. I have commented in the code below to show and explain this.
I'm totally open to the idea I've approached this wrong. How can I make this work?
DECLARE #Date_From AS DATETIME;
DECLARE #Date_To AS DATETIME;
SET #Date_From = '2016-10-01 00:00:00.000';
SET #Date_To = GETDATE() ;
-- Start Main query
SELECT TOP 10
CCustomer.Customer_Type AS 'Industry'
,SShipper.Ship_Date AS 'Ship_Date'
,SSContainer.Serial_No AS 'Serial_No'
,PPart.Grade AS 'Alloy'
,tbl_ScrapValue.Scrap_Value_per_lb AS 'Scap_Value_per_lb'
FROM
Sales_v_Shipper_Line AS SSLine
JOIN Sales_v_Shipper AS SShipper
ON SShipper.Shipper_Key = SSLine.Shipper_Key
JOIN Part_v_Part AS PPart
ON SSLine.Part_Key = PPart.Part_Key
JOIN Common_v_Customer AS CCustomer
ON SShipper.Customer_No = CCustomer.Customer_No
-- >>>>>>HELP NEEDED STARTING HERE
-- Below is the sub query that pulls the scrap sales value per pound.
-- The key point is that both shipments to our customers of real parts,
-- and the 'shipments' of scrap metal sales come from the same tables,
-- mainly Part_v_Part and Sales_v_Shipper, because of that the other
--solutions for the 'join by closest date' in the forums don't work.
LEFT OUTER JOIN (SELECT
MONTH(SShipper.Ship_Date) AS 'Scrap_Ship_Month'
,YEAR(SShipper.SHip_Date) AS 'Scrap_Ship_Year'
,PPart.Grade AS 'Alloy'
,AVG(AARIDist.Unit_Price) AS 'Scrap_Value_per_lb'
FROM
Sales_v_Shipper AS SShipper
JOIN Sales_v_Shipper_Line AS SS_Line
ON SShipper.Shipper_Key = SS_Line.Shipper_Key
JOIN Part_v_Part AS PPart
ON SS_Line.Part_Key = PPart.Part_Key
JOIN Common_v_Customer AS CCustomer
ON SShipper.Customer_No = CCustomer.Customer_No
WHERE CCustomer.Customer_Code = 'Scrap_Vendor'
AND SSHipper.Ship_Date <= #Date_To
GROUP BY
MONTH(SShipper.Ship_Date)
,YEAR(SShipper.SHip_Date)
,PPart.Grade
) AS tbl_ScrapValue
ON PPart.Grade = tbl_ScrapValue.Alloy
AND
YEAR(SShipper.Ship_Date) = YEAR(tbl_ScrapValue.Scrap_Ship_Year)
AND
MONTH(SShipper.Ship_Date) =(tbl_ScrapValue.Scrap_Ship_Month)
--- >>>>HELP NEEDED ENDS HERE
WHERE
AND SShipper.Ship_Date >= #Date_From
AND SSHipper.Ship_Date <= #Date_To
GROUP BY
SShipper.Shipper_No
,SShipper.Ship_Date
,CCustomer.Customer_Type
,SSContainer.Quantity
,PPart.Grade
Here's a sample output from the query above, as you can see the 'Scrap_Value_per_lb' is failing:
[![Sample_Output][1]][1]
Industry Ship_Date Serial_No Alloy Scap_Value_per_lb
Material Processing 17-Oct-16 4:47:00 PM S472091 C182 NULL
Material Processing 17-Oct-16 4:47:00 PM S472210 C182 NULL
Material Processing 17-Oct-16 4:47:00 PM S472211 C182 NULL
Electronics 17-Oct-16 4:27:00 PM S436738 C180 NULL
Electronics 17-Oct-16 4:27:00 PM S463290 C180 NULL
Electronics 17-Oct-16 4:27:00 PM S463315 C180 NULL
Electronics 17-Oct-16 4:27:00 PM S463327 C180 NULL
Electronics 17-Oct-16 4:27:00 PM S463333 C180 NULL
Electronics 17-Oct-16 4:27:00 PM S463345 C180 NULL
Electronics 17-Oct-16 4:27:00 PM S463354 C180 NULL
Update
This was edited a second time #7am 10/19/2016 to simplify code further, added comments in code to clarify based on feedback from others.
So, if I understand it correctly, you want the sub query to return only ONE row (Same for every row in the outer query?)
SELECT Top 1
SShipper.Ship_Date
,PPart.Grade AS 'Alloy'
,AARIDist.Unit_Price AS 'Scrap_Value_per_lb'
FROM
Sales_v_Shipper AS SShipper
JOIN Sales_v_Shipper_Line AS SS_Line
ON SShipper.Shipper_Key = SS_Line.Shipper_Key
JOIN Part_v_Part AS PPart
ON SS_Line.Part_Key = PPart.Part_Key
JOIN Common_v_Customer AS CCustomer
ON SShipper.Customer_No = CCustomer.Customer_No
JOIN Accounting_v_AR_Invoice_Dist AS AARIDist
ON SS_Line.Shipper_Line_Key = AARIDist.Shipper_Line_Key
WHERE CCustomer.Customer_Code = 'Scrap_Value'
AND SSHipper.Ship_Date <= #Date_To
AND AARIDIst.Unit_Price < AARIDist.Quantity
AND AARIDist.Unit_Price > '0'
Order By SShipper.Ship_Date Desc
) AS tbl_SValue
If the value of the sub query should be different for each row of the outer query, then I need to know how each row in the sub query is joined to the row in the outer query
So when you sell a metal of a certain alloy, you sell everything that's available, i.e. no remainders?
Then you take the records from the buying table and join them with the next selling record for the same alloy. This can be achieved with a cross apply. Here is a query with simple tables to give you the idea what's needed:
select
year(matched_sold.sold_date),
month(matched_sold.sold_date),
sum(bought.amount * bought.price)
from bought
cross apply
(
select top 1 *
from sold
where sold.alloy = bought.alloy
and sold.sold_date > bought.bought_date
order by sold.sold_date desc
) matched_sold
group by
year(matched_sold.sold_date),
month(matched_sold.sold_date);
I don't know, whether I got your problem right. You want to join two result sets by using a JOIN on a date field, where there are no exact matches guaranteed. Possibly you could use the ROW_NUMBER function to generate partitioned row numbers including a sort and then join on the row numbers with e.g. ROW_NR = 1.
TABLE 1 TABLE 2
ROW_NR DATE ID ROW_NR DATE ID
------ ---------- -- ------ ---------- --
1 10/25/2016 1 -match- 1 10/27/2016 1
2 10/24/2016 1
3 10/20/2016 1
4 10/19/2016 1
1 10/23/2016 2 -match- 1 10/28/2016 2
2 10/15/2016 2
3 10/09/2016 2
4 10/08/2016 2
Row Numbering in Table 1:
Data for TABLE1 with TABLE1.DATE <= TABLE2.DATE
Partitioned by ID
Sorted by ID and DATE DESC
Row Numbering in Table 2:
ROW_NR is always 1
With that you can join implicitly on the data fields without an exact match. Sorry for not providing a SQL Statement.
I spent a couple of hours thinking about this question and boiled the sample code down even further and re-posted the question here.
I think it makes more sense and I thank all of you who responded and tried to help answer what I wrote. It helped but I still could not get it to work.

SQL Server Unique Values

I have a SQL Server view that I have created that shows a list of companies and if they have a salesperson assigned, then it will be included, otherwise the value for salesperson will be blank.
When I run the query against this view, for the companies that have a salesperson assigned, I am getting 2 records, 1 with a blank Salesperson and another with the salesperson value.
What I am trying to get is a list with unique values for the company and if they have a salesperson assigned, then it would include that value and if the company doesn't have one, then the salesperson would be left blank.
The first table below shows what my current output is giving me. The 2nd table below shows an example of what I am trying to get. Any help is appreciated.
e.g.
Current Result
CustNO Company SalesPerson
ACM1 Acme Corp
ACM1 Acme Corp Bugs Bunny
ABC1 ABC Company
SSP1 Spacely Sprockets
SSP1 Spacely Sprockets Daffy Duck
SPN1 Springfield Nuclear
SPN1 Springfield Nuclear Porky Pig
Expected Result
CustNO Company SalesPerson
ACM1 Acme Corp Bugs Bunny
ABC1 ABC Company
SSP1 Spacely Sprockets Daffy Duck
SPN1 Springfield Nuclear Porky Pig
Here is the SQL statement for the View, not sure if it helps or not:
Create View SalesList as (
select distinct T1.CUSTNO as 'CustNo',
T1.COMPNAME as 'Company',
T1.SALESCODE as 'SalesID',
T2.EMPLNAME as 'Salesperson',
case
when T2.STATUS = 0 then 'Inactive'
when T2.STATUS = 1 then 'Active'
end as 'SalesStatus',
T2.SALESRATE as 'CommissionPct',
case
when T3.optfield = 'PARTNERAGENT' THEN T3.VALUE
else ''
end as 'AgentCode' ,
case
when T3.OPTFIELD = 'PARTNERAGENT' THEN T4.VDESC
else ''
end as 'AgentName'
from Table1 as T1
left join Table2 as T2 on T1.SALESCODE = T2.SALESCODE
left join Table3 as T3 on T1.CUSTNO = T3.CUSTNO
left join Table4 as T4 on T3.VALUE = T4.VALUE
where T2.EMPLOYEECD = 'PARTNER')
Hopefully this provides more information to be able to provide feedback. Thank you.
Without schema detail it's hard to give you an answer. Based on the screen caps provided I'll assume you're trying to perform aggregation, with non-empty Sales Person returned if any. As a trick, built-in function MAX may help.
SELECT
CustNo, Company, MAX("Sales Person") AS "Sales Person"
FROM
tableA
GROUP BY CustNo, Company

sql conversions in select statement

Here is a table example. using sql-server
I am trying to pull the max(login date) for each employee below, but the "login date" in the database is datatype int.
I can select it easy enough, ie,
select employee,title,max(login_date) from employee group by employee,title
I can use substring to convert the login_date("YYYYMM") to a date variable,
set #var_year=select (substring((select login_date),1,4))
using a variable to store the year and a variable to store the month, but how do I pass that to my main select statement?
Employee Title Login_Date
-----------------------------------------
Mike VP 201301
Amy CEO 201201
Joe Office Mgr 201105
Andy Admin Asst 201308
Joe Office Mgr 201205
Andy Admin Asst 201309
Joe Office Mgr 201205
Andy Admin Asst 201309
A few ways you could do that.
One would be
Select m.employee,m.title,
convert(int,substring(thisisNOTadate,1,4)) as [Year],
convert(int,substring(thisisNOTadate,5,2)) as [Month]
From
(select employee,title,max(login_date) as ThisisNOTadate
from employee group by employee,title
) m
Personally after slapping who ever called a string in the format YYYYMM login_date a few times, I'd rework the table so login_date was in fact a date with something like Convert(DateTime,Convert(VarChar(6), Login_date) + '01'), and then anything that needed parts of the date could use that standard date functions.
So based on your comment to Mr ReBand earlier then
select employee,title,
max(convert(datetime,Convert(varchar(6),login_date) + '01')) as arealdate
From employee group by employee,title
would be another.

T-SQL - Getting most recent date and most recent future date

Assume the table of records below
ID Name AppointmentDate
-- -------- ---------------
1 Bob 1/1/2010
1 Bob 5/1/2010
2 Henry 5/1/2010
2 Henry 8/1/2011
3 John 8/1/2011
3 John 12/1/2011
I want to retrieve the most recent appointment date by person. So I need a query that will give the following result set.
1 Bob 5/1/2010 (5/1/2010 is most recent)
2 Henry 8/1/2011 (8/1/2011 is most recent)
3 John 8/1/2011 (has 2 future dates but 8/1/2011 is most recent)
Thanks!
Assuming that where you say "most recent" you mean "closest", as in "stored date is the fewest days away from the current date and we don't care if it's before or after the current date", then this should do it (trivial debugging might be required):
SELECT ID, Name, AppointmentDate
from (select
ID
,Name
,AppointmentDate
,row_number() over (partition by ID order by abs(datediff(dd, AppointmentDate, getdate()))) Ranking
from MyTable) xx
where Ranking = 1
This usese the row_number() function from SQL 2005 and up. The subquery "orders" the data as per the specifications, and the main query picks the best fit.
Note also that:
The search is based on the current date
We're only calculating difference in days, time (hours, minutes, etc.) is ignored
If two days are equidistant (say, 2 before and 2 after), we pick one randomly
All of which could be adjusted based on your final requirements.
(Phillip beat me to the punch, and windowing functions are an excellent choice. Here's an alternative approach:)
Assuming I correctly understand your requirement as getting the date closest to the present date, whether in the past or future, consider this query:
SELECT t.Name, t.AppointmentDate
FROM
(
SELECT Name, AppointmentDate, ABS(DATEDIFF(d, GETDATE(), AppointmentDate)) AS Distance
FROM Table
) t
JOIN
(
SELECT Name, MIN(ABS(DATEDIFF(d, GETDATE(), AppointmentDate))) AS MinDistance
FROM Table
GROUP BY Name
) d ON t.Name = d.Name AND t.Distance = d.MinDistance

Resources