Query to get a running total of an "inventory change" column - sql

I have a table in MS Access that looks like this
What I'm trying to achieve is to get a 6th field that shows the sum of all changes up until that date.
I've achieved this easily in Excel by adding a SUM IF calculation, to illustrate what I need help with.
Now I need to remove Excel and have this done directly in Access.

Consider a subquery for a running sum of Inventory:
Conditionally aggregate subquery
SELECT t1.[Date], t1.[Part SKU], t1.[SKU Name], t1.[SumOfInventory Change],
(SELECT SUM(t2.[SumOfInventory Change])
FROM [Inventory Report Table] t2
WHERE t1.[Date] >= t2.[Date]
AND t1.[Part SKU] = [t2.Part SKU]) AS [Inventory Level]
FROM [Inventory Report Table] t1
DSum() Subquery (using dynamic criteria for date condition)
SELECT t1.[Date], t1.[Part SKU], t1.[SKU Name], t1.[SumOfInventory Change],
DSum("[SumOfInventory Change]", "[Inventory Report Table]",
"[Date] >=#" & t1.[Date] & "# AND [Part SKU] ='" & [t2.Part SKU] &"'") AS [Inventory Level]
FROM [Inventory Report Table] t1
If [Date] is stored as string and not date/time (from your post Access ACE/Jet does not by default store dates in that format which the other RDMS's do), replace # with single quotes.

Thanks to the user Utsav who made me aware of Dsum, I managed to make this DSum function, which solves my problem.
Expr1: DSum("[SumOfInventory Change]";
"Inventory Report Table";
"[Part SKU] = '" & [Part SKU] &
"' AND Date Between #" & Format("2015-01-01";"mm/dd/yyyy") &
"# And #" & Format([Date];"mm/dd/yyyy") & "#"
)

Related

SQL in MS Access (sum data in a secondary table based on it being between two dates + matching names in records)

I have two tables:
Salary Payslips:
Payslip ID
Salary Deduction
Payslip Date
Employee Name
1
calc
5/29/2022
ABC
2
calc
4/29/2022
ABC
3
calc
3/29/2022
ABC
4
calc
2/28/2022
ABC
Salary Deductions:
Deduction ID
Deduction Date
Deduction Amount
Employee Name
1
3/30/2022
50
ABC
2
5/10/2022
100
ABC
3
5/15/2022
100
ABC
I have two tables "Salary Payslips" and "Salary Deductions." Deductions are removed from the total payslip amount (not shown here because its unnecessary to address the problem).
I am trying to calculate the total accrued deductions per salary cycle. (4/29/2022 -> 5/29/2022 is one salary cycle). In the example above, the total deductions between the two dates (i.e. the salary cycle) should be 200. I would want to have that number show up in the Salary Deduction field with Payslip ID=1 in the Salary Payslips table.
In the same fashion, the deduction on 3/30/2022 should show up in the salary payslips table at the record with payslip id =2.... and so on. The deduction amount in the payslip id = 3 should be zero since there were no deductions in during the period between 2/28-3/29.
This should be done where the employee name is identical in both tables, so "ABC has 50 deducted from salary in cycle between 2/28/2022 and 3/29/2022" etc.
All of this should be updated recursively in a form. Hence, the control should be able to query the tables for the data and parse that onto the corresponding field in the form (and by association, the table).
Consider the DSum domain aggregate which is available as an expression function, VBA function, and SQL function in MS Access (frontend GUI but not backend connection). Similarly, you can use DLookUp (which I advised on your previous question) calling SUM() in expression argument.
Specifically, sum the Salary Deduction column in other table by corresponding Employee Name and where Deduction Date falls within date range of PaySlip Date and less than one month after using DateAdd.
Expression (set to control source of [Salary Deduction] textbox in form design of [Salary PaySlip] form)
=DSum("[Deduction Amount]",
"[Salary Deductions]",
"[Employee Name] = '" & [Employee Name] & "' AND
[Deduction Date] >= #" & [PaySlip Date] & "#
AND < #" & DateAdd("m", 1, [PaySlip Date]) & "#")
VBA (programmatically calculate control source)
Forms![Salary PaySlip]![Salary Deduction] = DSum( _
"[Deduction Amount]", _
"[Salary Deductions]", _
"[Employee Name] = '" & Forms![Salary PaySlip]![Employee Name] & "' AND " _
& "[Deduction Date] >= #" & Forms![Salary PaySlip]![PaySlip Date] & "#" _
& " AND < #" & DateAdd("m", 1, Forms![Salary PaySlip]![PaySlip Date]) & "#" _
)
SQL
UPDATE (using DSum to save data to table –cannot use subquery)
UPDATE [Salary PaySlip] p
SET p.[Salary Deduction] = DSum(
"[Deduction Amount]",
"[Salary Deductions]",
"[Employee Name] = '" & p.[Employee Name] & "' AND
[Deduction Date] >= #" & p.[PaySlip Date] & "#
AND < #" & DateAdd("m", 1, p.[PaySlip Date]) & "#"
)
Alternatively, you can use a correlated aggregate subquery which can have performance issues for large enough data. Hopefully, one day soon the MS Access team will add support for window functions (per SQL ANSI 2003) to the Access SQL dialect!
SELECT (using correlated aggregate subquery)
SELECT p.[PaySlip ID],
(SELECT SUM([Deduction Amount])
FROM [Salary Deductions] d
WHERE d.[Employee Name] = p.[Employee Name]
AND d.[Deduction Date] >= p.[PaySlip Date]
AND < DateAdd('m', 1, p.[PaySlip Date])
) AS [Salary Deduction],
p.[PaySlip Date]
p.[Employee Name]
FROM [Salary Payslips] p

Get the sum of a Sum column in sql server 2012

I want to add a row and add the value of a sum column. . see below code.
thanks
` SELECT
TicketSales.DeviceID AS [Device ID]
,COUNT(TicketSales.TicketID) AS [Total Issued Tickets]
,SUM(TicketSales.TotalPrice) AS [Total Amount]
(Here I want to add row to get the sum of SUM(TicketSales.TotalPrice) As [Total Amount])
FROM dbo.TicketSales
WHERE CONVERT(VARCHAR, TicketSales.StartTime,101) BETWEEN '" & fromDate & "' AND '" & toDate & "'
GROUP BY TicketSales.DeviceID`
Try with a windowed SUM() with the actual SUM() from the GROUP BY.
SELECT
TicketSales.DeviceID AS [Device ID]
,COUNT(TicketSales.TicketID) AS [Total Issued Tickets]
,SUM(TicketSales.TotalPrice) AS [Total Amount]
,SUM(SUM(TicketSales.TotalPrice)) OVER()
FROM dbo.TicketSales
WHERE CONVERT(VARCHAR, TicketSales.StartTime,101) BETWEEN '" & fromDate & "' AND '" & toDate & "'
GROUP BY TicketSales.DeviceID`
The OVER() clause makes the outmost SUM() a windowed sum, so it can be applied with the grouped columns or aggregate results.
EDIT: If you want it as a row you need to UNION it.
;WITH Aggregates AS
(
SELECT
TicketSales.DeviceID AS [Device ID]
,COUNT(TicketSales.TicketID) AS [Total Issued Tickets]
,SUM(TicketSales.TotalPrice) AS [Total Amount]
FROM dbo.TicketSales
WHERE CONVERT(VARCHAR, TicketSales.StartTime,101) BETWEEN '" & fromDate & "' AND '" & toDate & "'
GROUP BY TicketSales.DeviceID`
)
SELECT
A.[Device ID],
A.[Total Issued Tickets],
A.[Total Amount]
FROM
Aggregates AS A
UNION ALL
SELECT
[Device ID] = NULL,
[Total Issued Tickets] = SUM(A.[Total Issued Tickets]),
[Total Amount] = SUM(A.[Total Amount])
FROM
Aggregates AS A

Parameter values

can anyone tell me why the following query is asking me for parameter values. It should be (or at least I want to) populating the table with data 'from' where it's pulling it from:
SELECT [BILLING_REJECTS_orig].[ORG NBR] AS BK,
[BILLING_REJECTS_orig].[ACCOUNT NUMBER] AS ACCT,
[BILLING_REJECTS_orig].APPL AS AP,
[BILLING_REJECTS_orig].[ACCOUNT NAME] AS [ACCT NAME],
[BILLING_REJECTS_orig].[TMO NAME],
IIf(Len(DatePart("m", [BILLING_REJECTS_orig]![REPORT DATE])) = 1, Year([BILLING_REJECTS_orig]![REPORT DATE]) & "-" & "0" & Month([BILLING_REJECTS_orig]![REPORT DATE]), Year([BILLING_REJECTS_orig]![REPORT DATE]) & "-" & Month([BILLING_REJECTS_orig]![REPORT DATE])) AS [ACTIVITY MONTH]
INTO Billing_Rejects_Orig
FROM dbo_BILLING_REJECTS_DEPT,
TM_Report_Date
WHERE (
((Year([Billing_Rejects_Orig]![REPORT DATE])) = Year([TM_Report_Date]![Report_Date]))
AND ((Month([Billing_Rejects_Orig]![REPORT DATE])) = Month([TM_Report_Date]![Report_Date]))
);
As #WEI_DBA points out with incorrect reference to table, consider using table aliases as shown with b and t. This cuts down SQL code and is a helpful tool in maintainability as you can then switch out the table name in FROM or JOIN clauses without a whole re-write of query, assuming same structured table.
Especially in MS Access being a default backend database (Jet/ACE) that can be switched out with other RDBMS's (SQL Server, MySQL, etc.) and occasionally used for prototyping, table aliases can help in migration between both linked and local tables.
SELECT b.[ORG NBR] AS BK,
b.[ACCOUNT NUMBER] AS ACCT,
b.APPL AS AP,
b.[ACCOUNT NAME] AS [ACCT NAME],
b.[TMO NAME],
IIf(Len(DatePart('m', b.[REPORT DATE])) = 1,
Year(b.[REPORT DATE]) & '-' & '0' & Month(b.[REPORT DATE]),
Year(b.[REPORT DATE]) & '-' & Month(b.[REPORT DATE])) AS [ACTIVITY MONTH]
INTO Billing_Rejects_Orig
FROM dbo_BILLING_REJECTS_DEPT b,
TM_Report_Date t
WHERE (
((Year(b.[REPORT DATE])) = Year(t.[Report_Date]))
AND ((Month(b.[REPORT DATE])) = Month(t.[Report_Date]))
);

Limiting results in SSRS SQL query

I am trying to build a report on our ticketing system using SSRS. I am bringing in fields from both an "incident body" table and an "incident details" table. What I am looking for is whether the ticket has slipped past an SLA for response to the customer.
I am checking for tickets past SLA two ways:
1) the ticket is past the SLA date and there is no detail record matching the predetermined types
2) the ticket has a detail record matching the predetermined types but it was after the SLA date
I was able to build the CTE section below but I am struggling with how to reduce the result of that to just the oldest matching row/record. A ticket should only show up once in the report.
For example:
Johnny Blogs updates a ticket using the CONTACTED_TM recordtype on 1/1/2016. The SLA was 12/31/2015. The next day (1/2/2016) he also emails the TM using the EMAILOUT recordtype. The CTE returns both these rows/records. I only want the first result.
My query code is below. I have tried using a SELECT TOP 1 in the CTE, but it just causes it to return no results.
WITH CTE (Date, [Action ID], Description, Note, [Login ID], [Seq.Group], [Incident #], ResponseDue) AS
(
SELECT Date, [Action ID], [Incident Details].Description, [Incident Details].Note, [Login ID], [Incident Details].[Seq.Group], [Incident Details].[Incident #], [Incident].[Due Date & Time:] as ResponseDue
FROM [_SMDBA_].[Incident Details]
JOIN [_SMDBA_].[Incident]
ON [Incident Details].[Incident #] = [Incident].[Incident #]
WHERE ([Action ID] = 'CONTACTED_TM' OR [Action ID] = 'TM_UNAVAILABLE' OR ([Action ID] = N'EMAILOUT' AND _SMDBA_.[Incident].[Client Email] in ([Email To Email From])))
--AND Date >= Incident.[Due Date & Time:]
)
SELECT
I.[Incident #]
,I.[Group Name]
,I.[Seq.Group] as IncidentGroupSeq
,I.[Due Date & Time:] as ResponseDue
,I.[Priority ID:]
,I.[Priority Duration]
,I.[Subject ID]
,I.[Open Date & Time] as OpenDate
,I.[Impact ID:] as Impact
,I.[Urgency ID:] as Urgency
,CTE.[Action ID]
,CTE.Description
,CTE.Note
,CTE.[Login ID]
,CTE.Date AS IncidentDetailsDate
,CTE.[Seq.Group] as DetailsGroupSeq
,_SMDBA_.CalcWorkingSeconds(1001,[Due Date & Time:],CTE.Date) as WorkingSecs
,CASE
WHEN CTE.date >= I.[Due Date & Time:] THEN 'Response was Late'
WHEN CTE.Date IS NULL THEN 'There was no response'
WHEN CTE.date <= I.[Due Date & Time:] THEN 'Met SLA'
WHEN I.[Due Date & Time:] IS NULL THEN 'No Response date set'
END AS SLAStatus
FROM [_SMDBA_].Incident I
LEFT OUTER JOIN CTE
ON CTE.[Incident #] = I.[Incident #]
WHERE
I.[Open Date & Time] >= #StartDate
AND I.[Open Date & Time] <= #EndDate
AND I.[Group Name] in (#Group)
AND I.[Impact ID:] in (#Impact)
AND I.[Urgency ID:] in (#Urgency)
AND I.[Opened Group:] = 'SERVICE DESK'
Something like this, as referenced in the comments.
WITH CTE ([Date], [Action ID], Description, Note, [Login ID], [Seq.Group], [Incident #], ResponseDue) AS
(
SELECT
[Date],
[Action ID],
[Incident Details].Description,
[Incident Details].Note,
[Login ID],
[Incident Details].[Seq.Group],
[Incident Details].[Incident #],
[Incident].[Due Date & Time:] as ResponseDue,
row_number() over (partition by [Incident Details].[Incident #] order by [Date] desc) as RN
FROM [_SMDBA_].[Incident Details]
JOIN
[_SMDBA_].[Incident]
ON [Incident Details].[Incident #] = [Incident].[Incident #]
WHERE
([Action ID] = 'CONTACTED_TM' OR [Action ID] = 'TM_UNAVAILABLE' OR
([Action ID] = N'EMAILOUT' AND _SMDBA_.[Incident].[Client Email] in ([Email To Email From])))
--AND Date >= Incident.[Due Date & Time:]
)
SELECT
I.[Incident #]
,I.[Group Name]
,I.[Seq.Group] as IncidentGroupSeq
,I.[Due Date & Time:] as ResponseDue
,I.[Priority ID:]
,I.[Priority Duration]
,I.[Subject ID]
,I.[Open Date & Time] as OpenDate
,I.[Impact ID:] as Impact
,I.[Urgency ID:] as Urgency
,CTE.[Action ID]
,CTE.Description
,CTE.Note
,CTE.[Login ID]
,CTE.Date AS IncidentDetailsDate
,CTE.[Seq.Group] as DetailsGroupSeq
,_SMDBA_.CalcWorkingSeconds(1001,[Due Date & Time:],CTE.Date) as WorkingSecs
,CASE
WHEN CTE.date >= I.[Due Date & Time:] THEN 'Response was Late'
WHEN CTE.Date IS NULL THEN 'There was no response'
WHEN CTE.date <= I.[Due Date & Time:] THEN 'Met SLA'
WHEN I.[Due Date & Time:] IS NULL THEN 'No Response date set'
END AS SLAStatus
FROM [_SMDBA_].Incident I
LEFT OUTER JOIN CTE
ON CTE.[Incident #] = I.[Incident #]
WHERE
I.[Open Date & Time] >= #StartDate
AND I.[Open Date & Time] <= #EndDate
AND I.[Group Name] in (#Group)
AND I.[Impact ID:] in (#Impact)
AND I.[Urgency ID:] in (#Urgency)
AND I.[Opened Group:] = 'SERVICE DESK'
AND CTE.RN = 1

Querying from multiple linked Excel files

I currently have linked multiple Excel files to Access (one file for each month) and I want to create a query from each file that will give me a "master query." All the files are exactly the same with the headings and format. I cannot merge all three and then link because it is well over the Excel Maximum.
I tried this and it gives me the error Data type mismatch in criteria expression
SELECT [Created Date], [Store Name], ProductID, [Customer First Name] & " " & [Customer Last Name] AS CustomerName
FROM August2015
WHERE ProductID = 1587996 OR ProductID = 1587985
UNION
SELECT [Created Date], [Store Name], ProductID, [Customer First Name] & " " & [Customer Last Name] AS CustomerName
FROM July2015
WHERE ProductID = 1587996 OR ProductID = 1587985
UNION
SELECT [Created Date], [Store Name], ProductID, [Customer First Name] & " " & [Customer Last Name] AS CustomerName
FROM June2015
WHERE ProductID = 1587996 OR ProductID= 1587985
Can someone point me in the right direction. Thanks.
Sounds like ProductID is a string. If so:
WHERE ProductID = '1587996' OR ProductID = '1587985'