SQL Grand total without subtotals - sql

I'm making a large SQL report in Orderwise, very roughly simplified as follows;
SELECT Supplier.SupplierName, POHeader.PODate, POHeader.PORef, POLine.LineID, SUM(Subquery.Val)
FROM Supplier INNER JOIN POHeader ON POheader.supplier = Supplier.SupplierID
INNER JOIN POLine ON POLine.HeaderID = POHeader.PO_ID
INNER JOIN Subquery on Subquery.POLine = POLine.Line_ID
GROUP BY Supplier.SupplierName, POHeader.PODate, POHeader.PORef, POLine.Line_ID
I want a grand total at the bottom, without a bunch of subtotals dotted in throughout the report - therefore I don't think I can use ROLLUP. The Subquery in there is of course a sub query and in the real thing there will be twelve of them and all pretty complex, so I want to avoid a UNION just to total everything up if at all possible. Is there any other way I can put a Grand total row at the bottom of the report without subtotals?
Not completely sure of the SQL version but if it helps, google tells me Microsoft SQL Server Express or SQL Server Standard can be used with OrderWise

Use GROUPING SETS:
SELECT Supplier.SupplierName, POHeader.PODate, POHeader.PORef, POLine.LineID, SUM(Subquery.Val)
FROM Supplier INNER JOIN
POHeader
ON POheader.supplier = Supplier.SupplierID JOIN
POLine
ON POLine.HeaderID = POHeader.PO_ID JOIN
Subquery
ON Subquery.POLine = POLine.Line_ID
GROUP BY GROUPING SETS ( (Supplier.SupplierName, POHeader.PODate, POHeader.PORef, POLine.Line_ID), () )

Related

If transaction within date range, then return customer name (and not all the transactions!)

This code is taking a significant amount of time to run. It's returning every single transaction within the date range but I just need to know if the customer has had at least one transaction, then include the CustomerID, CustomerName, Type, Sign, ReportingName.
I think I need to GROUP BY 'CustomerID' but again only if there was a transaction within the date range. And of course, I'm sure there is an optimal way to execute the below TSQL because it's quite slow at present.
Thanks in advance for any help!
SELECT [ABC].[dbo].[vwPrimary].[RelatedNameId] AS CustomerID
,[ABC].[dbo].[vwPrimary].[RelatedName] AS CustomerName
,[AFGPurchase].[IvL].[TaxTreatment].[ParticluarType] AS Type
,[AFGPurchase].[IvL].[Product].[Sign] AS [Sign]
,[AFGPurchase].[IvL].[Product].[ReportingName] AS ReportingName
,[AFGPurchase].[IvL].[Transaction].[EffectiveDate] AS 'Date'
FROM (((([AFGPurchase].[IvL].[Account]
INNER JOIN [AFGPurchase].[IvL].[Position] ON [AFGPurchase].[IvL].[Account].[AccountId] = [AFGPurchase].[IvL].[Position].[AccountId])
INNER JOIN [AFGPurchase].[IvL].[Product] ON [AFGPurchase].[IvL].[Position].[ProductID] = [AFGPurchase].[IvL].[Product].[ProductId])
INNER JOIN [ABC].[dbo].[vwPrimary] ON [AFGPurchase].[IvL].[Account].[ReportingEntityId] = [ABC].[dbo].[vwPrimary].[RelatedNameId])
INNER JOIN [AFGPurchase].[IvL].[TaxTreatment] ON [AFGPurchase].[IvL].[Account].[TaxTreatmentId] = [AFGPurchase].[IvL].[TaxTreatment].[TaxTreatmentId])
INNER JOIN [AFGPurchase].[IvL].[Transaction] ON [AFGPurchase].[IvL].[Position].[PositionId] = [AFGPurchase].[IvL].[Transaction].[PositionId]
WHERE ((([AFGPurchase].[IvL].[TaxTreatment].[RegistrationType]) LIKE 'NON%')
AND (([AFGPurchase].[IvL].[Product].[Sign])='XYZ2')
AND (([AFGPurchase].[IvL].[Position].[Quantity])<>0)
AND (([AFGPurchase].[IvL].[Transaction].[EffectiveDate]) between '2021-12-31' and '2022-12-31'))
Check your indexes on fragmentation, to speed up your query. And make sure you have indexes.
If you just need one result, just TOP 1
SELECT TOP 1 [ABC].[dbo].[vwPrimary].[RelatedNameId] AS CustomerID
,[ABC].[dbo].[vwPrimary].[RelatedName] AS CustomerName
,[AFGPurchase].[IvL].[TaxTreatment].[ParticluarType] AS Type
,[AFGPurchase].[IvL].[Product].[Sign] AS [Sign]
,[AFGPurchase].[IvL].[Product].[ReportingName] AS ReportingName
,[AFGPurchase].[IvL].[Transaction].[EffectiveDate] AS 'Date'
FROM (((([AFGPurchase].[IvL].[Account]
INNER JOIN [AFGPurchase].[IvL].[Position] ON [AFGPurchase].[IvL].[Account].[AccountId] = [AFGPurchase].[IvL].[Position].[AccountId])
INNER JOIN [AFGPurchase].[IvL].[Product] ON [AFGPurchase].[IvL].[Position].[ProductID] = [AFGPurchase].[IvL].[Product].[ProductId])
INNER JOIN [ABC].[dbo].[vwPrimary] ON [AFGPurchase].[IvL].[Account].[ReportingEntityId] = [ABC].[dbo].[vwPrimary].[RelatedNameId])
INNER JOIN [AFGPurchase].[IvL].[TaxTreatment] ON [AFGPurchase].[IvL].[Account].[TaxTreatmentId] = [AFGPurchase].[IvL].[TaxTreatment].[TaxTreatmentId])
INNER JOIN [AFGPurchase].[IvL].[Transaction] ON [AFGPurchase].[IvL].[Position].[PositionId] = [AFGPurchase].[IvL].[Transaction].[PositionId]
WHERE ((([AFGPurchase].[IvL].[TaxTreatment].[RegistrationType]) LIKE 'NON%')
AND (([AFGPurchase].[IvL].[Product].[Sign])='XYZ2')
AND (([AFGPurchase].[IvL].[Position].[Quantity])<>0)
AND (([AFGPurchase].[IvL].[Transaction].[EffectiveDate]) between '2021-12-31' and '2022-12-31'))
If you only need to check for the existence of a row, and not actually get any data from it then use EXISTS() rather than INNER JOIN, e.g.
SELECT vpr.[RelatedNameId] AS CustomerID
,vpr.[RelatedName] AS CustomerName
,tt.[ParticluarType] AS Type
,prd.[Sign]
,prd.ReportingName
,tr.[EffectiveDate] AS [Date]
FROM [AFGPurchase].[IvL].[Account] AS acc
INNER JOIN [AFGPurchase].[IvL].[Position] AS pos ON acc.[AccountId] = pos.[AccountId]
INNER JOIN [AFGPurchase].[IvL].[Product] AS prd ON pos.[ProductID] = prd.[ProductId]
INNER JOIN [ABC].[dbo].[vwPrimary] AS vpr ON acc.[ReportingEntityId] = vpr.[RelatedNameId]
INNER JOIN [AFGPurchase].[IvL].[TaxTreatment] AS tt ON acc.[TaxTreatmentId] = tt.[TaxTreatmentId]
WHERE tt.[RegistrationType] LIKE 'NON%'
AND prd.[Sign]='XYZ2'
AND pos.[Quantity]<>0
AND EXISTS
( SELECT 1
FROM [AFGPurchase].[IvL].[Transaction] AS tr
WHERE tr.[PositionId] = pos.[PositionId]
AND tr.[EffectiveDate] BETWEEN '2021-12-31' AND '2022-12-31'
);
N.B. I have added in table aliases and removed all the unnecessary parentheses for readability - you may disagree that it is more readable, but I would expect that most people would agree
This may not offer any performance benefits over simply grouping by the columns you are selecting and keeping your joins as they are - SQL is after all a declarative language where you tell the engine what you want, not how to get it. So you may find that the two plans are the same because you are requesting the same result. Using EXISTS does have the advance of being more semantically tied to what you are trying to do though, so gives the optimiser the best chance of getting to the right plan. If you are still having performance issues, then you may need to inspect the execution plan, and see if it suggests any indexes.
Finally, if you are really still using SQL Server 2008 then you really need to start thinking about your upgrade path. It has been completely unsupported for over 3 years now.

Use group by with sum in query

These 3 tables that you see in the image are related
Course table and coaching table and sales table
I want to make a report from this table on how much each coach has sold by each course period.
The query I created is as follows, but unfortunately it has a problem and I do not know where the problem is.
Please help me fix the problem
Thank you
SELECT
dbo.tblCustomersOrders.id, dbo.tblCustomersOrders.pid, dbo.tblPost.postTitle,
dbo.tblArticleAuthor.authorName, SUM(dbo.tblCustomersOrders.prodPrice) AS TotalBuys
FROM
dbo.tblPost
INNER JOIN
dbo.tblArticleAuthor ON dbo.tblPost.id = dbo.tblArticleAuthor.articleID
INNER JOIN
dbo.tblCustomersOrders ON dbo.tblPost.id = dbo.tblCustomersOrders.pid
GROUP BY dbo.tblCustomersOrders.pid
For this use, SUM() is an Aggregate Function, so you need to refer all the
fields that you want to get in your result set.
Example:
SELECT
dbo.tblCustomersOrders.id, dbo.tblCustomersOrders.pid, dbo.tblPost.postTitle,
dbo.tblArticleAuthor.authorName, SUM(dbo.tblCustomersOrders.prodPrice) AS TotalBuys
FROM dbo.tblPost
INNER JOIN
dbo.tblArticleAuthor ON dbo.tblPost.id = dbo.tblArticleAuthor.articleID
INNER JOIN
dbo.tblCustomersOrders ON dbo.tblPost.id = dbo.tblCustomersOrders.pid
GROUP BY dbo.tblCustomersOrders.id, dbo.tblCustomersOrders.pid,
dbo.tblPost.postTitle, dbo.tblArticleAuthor.authorName
But this query does not solve the need for your report.
If you just need to get "how much each coach has sold by each course" , you can try the query bellow.
SELECT
dbo.tblArticleAuthor.authorName, dbo.tblPost.postTitle,
SUM(dbo.tblCustomersOrders.prodPrice) AS TotalBuys
FROM dbo.tblPost
INNER JOIN
dbo.tblArticleAuthor ON dbo.tblPost.id = dbo.tblArticleAuthor.articleID
INNER JOIN
dbo.tblCustomersOrders ON dbo.tblPost.id = dbo.tblCustomersOrders.pid
GROUP BY dbo.tblArticleAuthor.authorName, dbo.tblPost.postTitle
If you need, send more details regarding the desired result.
Here you can find more information about SQL SERVER Aggregate Functions:
https://learn.microsoft.com/en-us/sql/t-sql/functions/aggregate-functions-transact-sql?view=sql-server-ver15
And here a quick example regarding SQL Aliases to build queries with a simple
and effective way:
https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_alias_table
Per your description of the task, the problem is that you only GROUPed BY dbo.tblCustomersOrders.pid, which is the period's id I guess, but you also need to GROUP BY the coach, which is dbo.tblArticleAuthor.authorName, I guess again. Plus in the SELECT field list you can not use more columns only that are aggregated + GROUPed.

SQL Inner Join Combining Rows in Results - Too Few Results

I'm trying to create a report from four tables: Master, Details, Labor, Costs. Currently I have a working report pulling columns from Jobs, Details, and Costs. Shared key for all tables is JobNumber. I'm looking to get a sum of the costs and a sum of the hours for each job. Here is my code that works with three tables:
SELECT
JC_JobMaster.JobNumber,
JC_JobMaster.JobDescription1 AS Customer_Name,
JC_JobMaster.JobDescription2 AS Work,
JC_JobSortCategories.SortCategory2 AS Work_Type,
JC_JobMaster.CustomerCode,
JC_JobMaster.ContractValue,
SUM(JC_JobBalancesCostDollars.CurrentPeriodAmount*-1) AS Gross_profit,
JC_JobSortCategories.SortCategory1 AS City,
JC_JobSortCategories.SortCategory4 AS Salesman
FROM
JC_JobMaster INNER JOIN
JC_JobSortCategories ON JC_JobMaster.JobNumber = JC_JobSortCategories.JobNumber INNER JOIN
JC_JobBalancesCostDollars ON JC_JobMaster.JobNumber = JC_JobBalancesCostDollars.JobNumber
WHERE
JC_JobMaster.JobNumber between '11566' and '13441' and
JC_JobSortCategories.SortCategory5 = 'AWARDED' and
JC_JobSortCategories.SortCategory2 = 'FPR' and
JC_JobSortCategories.SortCategory1 in ('1')
Group By
JC_JobMaster.JobNumber,
JC_JobMaster.JobDescription1,
JC_JobMaster.JobDescription2,
JC_JobSortCategories.SortCategory2,
JC_JobMaster.CustomerCode,
JC_JobMaster.ContractValue,
JC_JobSortCategories.SortCategory1,
JC_JobSortCategories.SortCategory4
Order by
JC_JobMaster.JobNumber
Everything works fine - this query returns 45 rows with easily-verified data. However, adding in a fourth table using an inner join INNER JOIN JC_JobCostDetailLabourHours ON JC_JobMaster.JobNumber = JC_JobCostDetailLabourHours.JobNumber only returns 7 rows, and the summed column Gross_profit is incorrect. Using a LEFT JOIN as opposed to INNER JOIN gives me the proper 45 rows, but the summed column is still incorrect. The LabourHours and BalancesCostDollars tables have multiple entries for each job number that I'm wanting summed. What am I doing wrong with adding the fourth table, or did I just happen to get the right result by accident with the first summed column?

SSRS 2008 R2 / SQL - How to filter groups but keep detail data?

EDIT - i'm reposting this question in an attempt to explain what i mean better
I'm using SQL 2008 R2 and I work for a retail department store and we need a report to show all the sales orders made in each department, and sections of those departments.
What i want is to group up all the sales order lines by department and section, but remove only the sections that have a total sales value of less than £50. I still want to see order lines that are over £50, though.
Here is an example of what i currently have:
Data before filtering
I want to remove the Accessories section and all lines contained within it, as it has a total section value of less than £50. So i would want it looking like this after filtering:
Data after filtering
Here is my code:
SELECT department.department_name
,section.section_name
,sales_order_detail.sales_order_number
,sales_order_detail.sales_order_line
,LineValue
FROM
sales_order_detail INNER JOIN stock_item ON sales_order_detail.stock_item_code = stock_item.stock_item_code
INNER JOIN style ON stock_item.style_code = style.style_code
INNER JOIN department ON style.dept_code = department.department_code
INNER JOIN section ON style.section_code = section.section_code AND style.dept_code = section.department_code AND department.department_code = section.department_code
Can you please explain all the ways this can be done. I've tried using GROUP BY and HAVING but that then filters out all my sales order lines. I've tried using a Group Filter in the visual studio report design surface which removes the lines but then aggregates calculated at the Department group scope don't take into account the lines removed at the section level.
I appreciate any help i can get on this.
Jacob
As you are using 2008R2, you can use the magic that are Windowed Functions to calculate the total of the group that the row belongs to (the partition part of the over clause below) and then wrap your query into a filtering select statement. Not having your data this is obviously not tested, but it should work:
select department_name
,section_name
,sales_order_number
,sales_order_line
,LineValue
,GroupTotal
from(
select d.department_name
,se.section_name
,sod.sales_order_number
,sod.sales_order_line
,sod.qty_ordered * sod.selling_price AS LineValue
,sum(sod.qty_ordered * sod.selling_price) over (partition by d.department_name
,se.section_name
) as GroupTotal
from sales_order_detail sod
inner join stock_item si
on sod.stock_item_code = si.stock_item_code
inner join style s
on stock_item.style_code = s.style_code
inner join department d
on s.dept_code = d.department_code
inner join section se
on s.section_code = se.section_code
and s.dept_code = se.department_code
and d.department_code = se.department_code
) a
where GroupTotal > 50

SQL Server 2008 Issue With GROUP BY

SELECT
ETRN_MFTransactionGroup.FolioNumber,
ETRN_MFTransactionGroup.PrimaryApplicantContactID,
ETRN_MFTransaction.PK_TransactionGroupID AS Expr1,
ETRN_MFTransaction.PK_SchemeProductID,
ETRN_MFTransaction.Units,
PA_E_CUSTOMER.dbo.ECUS_Contact.PK_ContactID,
PA_E_CUSTOMER.dbo.ECUS_Contact.UserID,
PA_E_CUSTOMER.dbo.ECUS_Contact.FName,
PA_E_CUSTOMER.dbo.ECUS_Contact.MName,
PA_E_CUSTOMER.dbo.ECUS_Contact.LName,
PA_E_INSTRUMENTS.dbo.EINS_MFSchemeProduct.PK_SchemeProductID AS Expr2,
PA_E_INSTRUMENTS.dbo.EINS_MFSchemeProduct.RegistrarCode,
ETRN_MFTransactionGroup.PK_TransactionGroupID
FROM
ETRN_MFTransactionGroup
INNER JOIN
ETRN_MFTransaction ON
ETRN_MFTransactionGroup.PK_TransactionGroupID = ETRN_MFTransaction.PK_TransactionGroupID
INNER JOIN
PA_E_CUSTOMER.dbo.ECUS_Contact ON
ETRN_MFTransactionGroup.PrimaryApplicantContactID =
PA_E_CUSTOMER.dbo.ECUS_Contact.PK_ContactID
INNER JOIN
PA_E_INSTRUMENTS.dbo.EINS_MFSchemeProduct ON
ETRN_MFTransaction.PK_SchemeProductID =
PA_E_INSTRUMENTS.dbo.EINS_MFSchemeProduct.PK_SchemeProductID
**GROUP BY
ETRN_MFTransactionGroup.FolioNumber,**
ETRN_MFTransactionGroup.PrimaryApplicantContactID,
ETRN_MFTransaction.PK_TransactionGroupID,
ETRN_MFTransaction.PK_SchemeProductID,
ETRN_MFTransaction.Units,
PA_E_CUSTOMER.dbo.ECUS_Contact.PK_ContactID,
PA_E_CUSTOMER.dbo.ECUS_Contact.UserID,
PA_E_CUSTOMER.dbo.ECUS_Contact.FName,
PA_E_CUSTOMER.dbo.ECUS_Contact.MName,
PA_E_CUSTOMER.dbo.ECUS_Contact.LName,
PA_E_INSTRUMENTS.dbo.EINS_MFSchemeProduct.PK_SchemeProductID,
PA_E_INSTRUMENTS.dbo.EINS_MFSchemeProduct.RegistrarCode,
ETRN_MFTransactionGroup.PK_TransactionGroupID
The query above works absolutely fine but i need to group only by FolioNumber (i.e - ETRN_MFTransactionGroup.FolioNumber). Grouping by rest of the fields not required at all!
If you don't want to group on the other fields, you need some aggregate function to tell SQL how to treat the multiple records. MAX, or MIN work well for varchar fields. SUM, AVERAGE work well for numerics.