T-SQL - Pivot by week - sql

I'm currently trying to create a T-SQL, which runs through a list of deliveries in a table, and groups them by the Customer and the Depot - so each row will be
Customer, Depot, Total Value (sum of a column called Rate)
However, the customer would like the 'total value' split into the last 9 weeks - so rather than total value, we'll have columns like this:
22/01/2012 29/01/2012 05/02/2012 12/02/2012 19/02/2012 26/02/2012 04/03/2012 11/03/2012 18/03/2012
The dates would of course change for when they run the query - it'll just be the last 9 weeks. They also want a column for the Average of all these.
I understand pivot may help me but I'm a bit stumped on how to do this. Here's my current query:
SELECT d.Name AS 'Depot, s.Name AS 'Customer', SUM(c.Rates) AS 'Total Value'
FROM Deliveries AS c INNER JOIN Account AS s ON c.Customer = s.ID
INNER JOIN Depots AS d ON c.CollectionDepot = d.Letter
GROUP BY d.Name, s.Name
Many thanks!
EDIT: Here's a screenshot of the data currently - we won't need the 'total' column on the end, just there to show you. The 'Date' column is present in the Deliveries table and is called TripDate

Without knowing your exact data. It hard to predict what you are getting. But I can give you a suggestion of a solution.
Table structure
CREATE TABLE Deliveries
(
Customer INT,
CollectionDepot INT,
Rates FLOAT,
TripDate DATETIME
)
CREATE TABLE Account
(
Name VARCHAR(100),
ID INT
)
CREATE TABLE Depots
(
Name VARCHAR(100),
Letter INT
)
Test data
INSERT INTO Deliveries
VALUES
(1,1,452,GETDATE()-10),
(1,1,800,GETDATE()-30),
(1,1,7895,GETDATE()-2),
(1,1,451,GETDATE()-2),
(1,1,478,GETDATE()-89),
(1,1,4512,GETDATE()-31),
(1,1,782,GETDATE()-20),
(1,1,652,GETDATE()-5),
(1,1,752,GETDATE()-452)
INSERT INTO Account
VALUES
('Customer 1',1)
INSERT INTO Depots
VALUES
('Depot 1',1)
Table that contains the ranges and the formated date
CREATE TABLE #tmp
(
StartDate DATETIME,
EndDate DATETIME,
FomatedDate VARCHAR(20)
)
Calculate the date ranges
;WITH Nbrs ( n ) AS (
SELECT 0 UNION ALL
SELECT 1+n FROM Nbrs WHERE n < 8 )
INSERT INTO #tmp
SELECT
DATEADD(WEEK,-n-1,GETDATE()),
DATEADD(WEEK,-n,GETDATE()),
convert(varchar, DATEADD(WEEK,-n,GETDATE()), 112)
FROM
Nbrs
ORDER BY
-n
The date columns for the pivot
DECLARE #cols VARCHAR(MAX)
SELECT #cols = COALESCE(#cols + ','+QUOTENAME(FomatedDate),
QUOTENAME(FomatedDate))
FROM
#tmp
Declaring some dynamic sql and executing it
DECLARE #query NVARCHAR(4000)=
N'SELECT
*
FROM
(
SELECT
Depots.Name AS Depot,
Account.Name AS Customer,
Deliveries.Rates,
tmp.FomatedDate,
AVG(Deliveries.Rates) OVER(PARTITION BY 1) AS Average,
SUM(Deliveries.Rates) OVER(PARTITION BY 1) AS Total
FROM
Deliveries
JOIN Account
ON Deliveries.Customer = Account.ID
JOIN Depots
ON Deliveries.CollectionDepot = Depots.Letter
JOIN #tmp AS tmp
ON Deliveries.TripDate BETWEEN tmp.StartDate AND tmp.EndDate
) AS p
PIVOT
(
AVG(rates)
FOR FomatedDate IN ('+#cols+')
) AS pvt'
EXECUTE(#query)
And then cleaning up after myself.
DROP TABLE Deliveries
DROP TABLE Account
DROP TABLE Depots
DROP TABLE #tmp

You would have to make use of the PIVOT Keyword which is available in your version of SQL Server. I have outlined how your query should look, of course some tweaking will be required since it is difficult to test without having a copy of your data.
SELECT Depots.Name AS 'Depot', Account.Name, '22/01/2012', '29/01/2012', '05/02/2012', '12/02/2012',
FROM
(SELECT Name,
FROM Deliveries
INNER JOIN Account ON Deliveries.Customer = Account.ID
INNER JOIN Depots ON Account.CollectionDepot) AS Source
PIVOT
(
SUM(Deliveries.Rates)
FOR Date IN ('22/01/2012', '29/01/2012', '05/02/2012', '12/02/2012')
) AS 'Pivot Table'
For reference you could use this as a guide:
http://msdn.microsoft.com/en-us/library/ms177410.aspx

Related

Copy Most Recent Date's row values where gaps in dates exist

I am creating a report in Tableau for a new product that captures metrics such as previous applications pending, new apps end of day pending etc. In order to do this, I need a a snapshot of the end of day status for each application each day. A decision was made above my pay grade to only capture a rolling seven day delta of the data. So, what happens is an application that has not had a status change in the previous seven days stops appearing in the DB until something new happens which allows for gaps in dates and throws my numbers off in my report. What I need is a snapshot for each day for each application, so when there is a date gap, I want to grab the most recent previous day's record and insert to fill in the gaps between the two dates. Also, I join to a credit score table and we sometimes pull all three bureaus, sometimes two, sometimes one so there could be up to three rows per application per day.
I have looked on this site for similar issues which I seem some similar issues however none are an exact match to what I am trying to accomplish and I honestly do not know where to start. Will a correlated subquery accomplish what I need? I provided some code below to show what the data looks like currently.
drop table if exists #date
drop table if exists #test
create table #date
(
calendar_date date
)
insert into #date
values
('2019-08-07'),
('2019-08-08'),
('2019-08-09'),
('2019-08-10'),
('2019-08-11'),
('2019-08-12')
create table #test
(
id int,
period_date date,
decision_status varchar(20),
credit_score int,
expired_flag bit
)
insert into #test (id,period_date,decision_status,credit_score,expired_flag)
values
(1,'2019-08-08','declined',635,null),
(1,'2019-08-08','declined',642,null),
(1,'2019-08-09','declined',635,null),
(1,'2019-08-09','declined',642,null),
(1,'2019-08-10','declined',635,null),
(1,'2019-08-10','declined',642,null),
(1,'2019-08-11','declined',635,null),
(1,'2019-08-11','declined',642,null),
(1,'2019-08-12','declined',635,null),
(1,'2019-08-12','declined',642,null),
(2,'2019-08-08','review',656,null),
(2,'2019-08-08','review',648,null),
(2,'2019-08-09','review',656,null),
(2,'2019-08-09','review',648,null),
(2,'2019-08-12','review',656,null),
(2,'2019-08-12','review',648,null),
(3,'2019-08-08','preapproved',678,null),
(3,'2019-08-08','preapproved',689,null),
(3,'2019-08-08','preapproved',693,null),
(3,'2019-08-09','preapproved',678,null),
(3,'2019-08-09','preapproved',689,null),
(3,'2019-08-09','preapproved',693,null),
(3,'2019-08-11','preapproved',678,1),
(3,'2019-08-11','preapproved',689,1),
(3,'2019-08-11','preapproved',693,1),
(3,'2019-08-12','preapproved',678,1),
(3,'2019-08-12','preapproved',689,1),
(3,'2019-08-12','preapproved',693,1),
(4,'2019-08-08','onboarded',725,null),
(4,'2019-08-09','onboarded',725,null),
(4,'2019-08-10','onboarded',725,null),
(5,'2019-08-08','approved',685,null),
(5,'2019-08-08','approved',675,null),
(5,'2019-08-09','approved',685,null),
(5,'2019-08-09','approved',675,null),
(5,'2019-08-12','approved',685,1),
(5,'2019-08-12','approved',675,1)
And the query:
select id, calendar_date, period_date, decision_status, credit_score, expired_flag
from #date join
#test
on calendar_date=dateadd(day,-1,period_date)
order by id, calendar_date
I just need each application to show for each day.
You may just need a left join: just need a left join:
select t.id, d.calendar_date, t.period_date, t.decision_status, t.credit_score, t.expired_flag
from #date d left join
#test t
on d.calendar_date = dateadd(day, -1, t.period_date)
order by id, d.calendar_date;
If by "application" you mean the id in #test, then use cross join to generate the rows and a outer apply to fill in the values:
select t.id, d.calendar_date, t.period_date, t.decision_status, t.credit_score, t.expired_flag
from #date d cross join
(select distinct id from #test) i outer apply
(select top (1) t.*
from #test t
where t.id = i.id and t.date <= d.date
order by t.date desc
) t
Update:
After receiving the reply from Gordon, which gave me some inspiration and set me in the right direction, and conducting some additional research, I appear to have found a solution that is working. I wanted to share the solution here in case anyone else runs across this problem. I am posting the code below:
drop table if exists #date
drop table if exists #test
drop table if exists #test1
drop table if exists #row_num
create table #date
(
calendar_date date
)
insert into #date
values
('2019-08-07'),
('2019-08-08'),
('2019-08-09'),
('2019-08-10'),
('2019-08-11')
create table #test
(
id int,
period_date date,
decision_status varchar(20),
credit_score int,
expired_flag bit
)
insert into #test (id,period_date,decision_status,credit_score,expired_flag)
values
(1,'2019-08-08','declined',635,null),
(1,'2019-08-08','declined',642,null),
(1,'2019-08-09','declined',635,null),
(1,'2019-08-09','declined',642,null),
(1,'2019-08-10','declined',635,null),
(1,'2019-08-10','declined',642,null),
(1,'2019-08-11','declined',635,null),
(1,'2019-08-11','declined',642,null),
(1,'2019-08-12','declined',635,null),
(1,'2019-08-12','declined',642,null),
(2,'2019-08-08','review',656,null),
(2,'2019-08-08','review',648,null),
(2,'2019-08-09','review',656,null),
(2,'2019-08-09','review',648,null),
(2,'2019-08-12','review',656,null),
(2,'2019-08-12','review',648,null),
(3,'2019-08-08','preapproved',678,null),
(3,'2019-08-08','preapproved',689,null),
(3,'2019-08-08','preapproved',693,null),
(3,'2019-08-09','preapproved',678,null),
(3,'2019-08-09','preapproved',689,null),
(3,'2019-08-09','preapproved',693,null),
(3,'2019-08-11','preapproved',678,1),
(3,'2019-08-11','preapproved',689,1),
(3,'2019-08-11','preapproved',693,1),
(3,'2019-08-12','preapproved',678,1),
(3,'2019-08-12','preapproved',689,1),
(3,'2019-08-12','preapproved',693,1),
(4,'2019-08-08','onboarded',725,null),
(4,'2019-08-09','onboarded',725,null),
(4,'2019-08-10','onboarded',725,null),
(5,'2019-08-08','approved',685,null),
(5,'2019-08-08','approved',675,null),
(5,'2019-08-09','approved',685,null),
(5,'2019-08-09','approved',675,null),
(5,'2019-08-12','approved',685,1),
(5,'2019-08-12','approved',675,1)
select id,calendar_date,decision_status,credit_score,expired_flag
,ROW_NUMBER() over(partition by id,calendar_date order by calendar_date) as row_id
,cast(ROW_NUMBER() over(partition by id,calendar_date order by calendar_date) as char(1)) as row_num
into #test1
from #date
join #test
on calendar_date=dateadd(day,-1,period_date)
order by id,calendar_date
create table #row_num
(
row_id int,
row_num char(1)
)
insert into #row_num
values
(1,'1'),
(2,'2'),
(3,'3')
select i.id
,d.calendar_date
,coalesce(t.decision_status,t1.decision_status) as decision_status
,coalesce(t.credit_score,t1.credit_score) as credit_score
,coalesce(t.expired_flag,t1.expired_flag) as expired_flag
from #date d
cross join
(select distinct id
from #test1 ) i
cross join #row_num r
left join #test1 t
on t.id=i.id
and t.row_id=r.row_id
and t.calendar_date=d.calendar_date
join
(select id,row_id,decision_status,credit_score,expired_flag
,calendar_date as start_date
,lead(calendar_date,1,dateadd(day,1,(select max(calendar_date) from #date)))
over (partition by id,row_id order by calendar_date) as end_date
from #test1
) t1
on t1.id=i.id
and t1.row_id=r.row_id
and d.calendar_date>=t1.start_date
and d.calendar_date<t1.end_date
order by i.id,d.calendar_date,r.row_id
This gives me what I am looking for, all the daily records for each application for each day.

Inserting data into table variable

I want to know if it's possible to insert data into a table variable from a select query. When using a variable table before I have inserted the values and typed them myself.
I have come up with the following select query that displays the latest comment for a repair order. It returns 37 records and I wanted to know if it's possible to insert those into a variable table.
SELECT
a.CH_REPREF, a.CH_DATE, a.CH_CRTIME,
b.[Latest Customer Comment], a.CH_CRUSER, a.CH_CCOMMNT
FROM
(SELECT
CH_REPREF,
MAX(CH_REPREF1) As [Latest Customer Comment]
FROM
dbo.V_CSRPCH
WHERE
CH_CCOMMNT IS NOT NULL
AND CH_CCOMMNT NOT LIKE 'X'
GROUP BY
CH_REPREF) b,
(SELECT
CH_REPREF, CH_DATE, CH_CRTIME, CH_REPREF1,
CH_CRUSER, CH_CCOMMNT, CH_ACCOUNT
FROM
dbo.V_CSRPCH) a
WHERE
CH_REPREF1 = [Latest Customer Comment]
AND CH_ACCOUNT = 'DDCHC'
I have started the table variable with the following, but am stuck whether it's possible to fill it with the data returned by the select query:
Declare #tbl_last_customer_comment TABLE
(CH_REPREF nvarchar(10),
CH_DATE smalldatetime,
CH_CRTIME nvarchar(8),
Latest int,
CH_CRUSER nvarchar(8),
CH_CCOMMNT text)
just do it as 'constant' table-
INSERT INTO #tbl_last_customer_comment
SELECT a.CH_REPREF, a.CH_DATE, a.CH_CRTIME,b.[Latest Customer Comment],a.CH_CRUSER,a.CH_CCOMMNT
FROM (SELECT CH_REPREF, MAX(CH_REPREF1) As [Latest Customer Comment]
FROM dbo.V_CSRPCH
WHERE CH_CCOMMNT IS Not Null AND CH_CCOMMNT Not Like 'X'
GROUP BY CH_REPREF) b,
(SELECT CH_REPREF, CH_DATE, CH_CRTIME,CH_REPREF1, CH_CRUSER, CH_CCOMMNT, CH_ACCOUNT
FROM dbo.V_CSRPCH) a
WHERE CH_REPREF1 = [Latest Customer Comment] AND CH_ACCOUNT = 'DDCHC'

MSSQL: How to get multiple values from another table

For a given "CustomerId" I need to get 4 related values from a column ("CompanySales") in another table.
I have joined the two tables and, with the query below, manage to get 2 "CompanySales" values from the column in the other table.
How do I do this do get 4 values (I need CompanySales for "WeekNumber" = 1,2,3 and 4)
This is the SQL query I have to secure "CompanySales" for "Weeknumber" = 1 and 2:
Declare #TempTable1 table
(
CustomerID INT,
CustomerName Varchar (50),
CompanySales DEC (8,2),
WeekNumber INT
)
INSERT INTO #TempTable1 ("CustomerID","CustomerName", "WeekNumber")
SELECT Customer.CustomerID, Customer.CustomerName, Company.WeekNumber, company.Sales
FROM Customer INNER JOIN
Company ON Customer.CustomerID = Company.CustomerID;
With tblDifference as
(
Select Row_Number() OVER (Order by WeekNumber) as RowNumber,CustomerID,CustomerName, companysales, WeekNumber from #TempTable1
)
Select Top (50) cur.CustomerID, Cur.CustomerName, Cur.WeekNumber as CurrentWeek, Prv.WeekNumber as PreviousWeek, Cur.CompanySales as CurrentSales, Prv.CompanySales as PreviousSales, CAST(((Cur.CompanySales-Prv.CompanySales)/Prv.CompanySales)*100 As Decimal(8,2)) as PercentChange from
tblDifference Cur Left Outer Join tblDifference Prv
On Cur.CustomerID=Prv.CustomerID
Where cur.WeekNumber = 1 AND prv.WeekNumber = 2
Order BY PercentChange ASC
How about adding between there cur.WeekNumber Between 1 and 4?

Finding Duplicate occurrences in column

I have 2 tables Customer and meter.
A customer might have multiple meternbr in meter table. Customer has a customernbr column.
I want to return customers who have more than one meternbr only. Look at the table below. I want to return only customers a and c with the meternbr also.
Customer Meter
-------- -----
a a-100
b a-101
c b-103
d c-104
c-105
If that is a single string (which i don't think is a good idea to begin with), and if your DBMS supports LEFT/SUBSTRING and INSTR you can do a LEFT or a SUBSTRING combined with a INSTR that finds where is the first '-' index and get the customers that have more than one occurrence by using GROUP BY and HAVING COUNT(*) > 1.
SELECT LEFT(meterColumn,INSTR(meterColumn,'-')-1)
FROM meter
WHERE LEFT(meterColumn,INSTR(meterColumn,'-')-1) IN (
SELECT LEFT(meterColumn,INSTR(meterColumn,'-')-1)
FROM meter
GROUP BY LEFT(meterColumn,INSTR(meterColumn,'-')-1)
HAVING COUNT(*) > 1
)
GROUP BY 1;
If those are two columns in the meter table (customerNbr) and (meterNbr), you could simply do:
SELECT customerNbr
FROM meter
GROUP BY 1
HAVING COUNT(*) > 1;
Use GROUP BY and HAVING Clause with COUNT(*) > 1.
Here's a working Sample: http://sqlfiddle.com/#!3/d1b91/17
Pasting code and results below also:
CREATE TABLES (Note: Have not placed FK Constaint for demo purposes)
CREATE TABLE Customer
(
customernbr NVARCHAR(20) NOT NULL
)
CREATE TABLE Meter
(
meternbr NVARCHAR(20) NOT NULL,
customernbr NVARCHAR(20) NOT NULL
)
INSERT DATA. (Uncomment last 2 SELECT statements if you wanna see the data)
INSERT INTO Customer VALUES
('a'),
('b'),
('c'),
('d');
INSERT INTO Meter VALUES
('a-100','a'),
('a-101','a'),
('b-103','b'),
('c-104','c'),
('c-105','c'),
('d-106','d');
--SELECT * FROM Customer;
--SELECT * FROM Meter;
RUN SELECT STATEMENT
SELECT
customernbr AS 'Customer',
meternbr AS 'Meter'
FROM Meter WHERE customernbr IN
(
SELECT customernbr
FROM Meter
GROUP BY customernbr
HAVING COUNT(*) > 1
)
SEE RESULTS :)
CUSTOMER METER
a a-100
a a-101
c c-104
c c-105

SUM by two different GROUP BY

I'm getting the wrong result from my report. Maybe i'm missing something simple.
The report is an inline table-valued-function that should count goods movement in our shop and how often these spareparts are claimed(replaced in a repair).
The problem: different spareparts in the shop-table(lets call it SP) can be linked to the same sparepart in the "repair-table"(TSP). I need the goods movement of every sparepart in SP and the claim-count of every distinct sparepart in TSP.
This is a very simplified excerpt of the relevant part:
create table #tsp(id int, name varchar(20),claimed int);
create table #sp(id int, name varchar(20),fiTsp int,ordered int);
insert into #tsp values(1,'1235-6044',300);
insert into #tsp values(2,'1234-5678',400);
insert into #sp values(1,'1235-6044',1,30);
insert into #sp values(2,'1235-6044',1,40);
insert into #sp values(3,'1235-6044',1,50);
insert into #sp values(4,'1234-5678',2,60);
WITH cte AS(
select tsp.id As TspID,tsp.name as TspName,tsp.claimed As Claimed
,sp.id As SpID,sp.name As SpName,sp.ordered As Ordered
from #sp sp inner join #tsp tsp
on sp.fiTsp=tsp.id
)
SELECT TspName, SUM(Claimed) As Claimed, Sum(Ordered) As Ordered
FROM cte
Group By TspName
drop table #tsp;
drop table #sp;
Result:
TspName Claimed Ordered
1234-5678 400 60
1235-6044 900 120
The Ordered-count is correct but the Claimed-count should be 300 instead of 900 for TspName='1235-6044'.
I need to group by Tsp.ID for the claim-count and group by Sp.ID for the order-count. But how in one query?
Edit: Actually the TVF looks like(note that getOrdered and getClaimed are SVFs and that i'm grouping in the outer select on TSP's Category):
CREATE FUNCTION [Gambio].[rptReusedStatistics](
#fromDate datetime
,#toDate datetime
,#fromInvoiceDate datetime
,#toInvoiceDate datetime
,#idClaimStatus varchar(50)
,#idSparePartCategories varchar(1000)
,#idSpareParts varchar(1000)
)
RETURNS TABLE AS
RETURN(
WITH ExclusionCat AS(
SELECT idSparePartCategory AS ID From tabSparePartCategory
WHERE idSparePartCategory IN(- 3, - 1, 6, 172,168)
), Report AS(
SELECT Cat.SparePartCategoryName AS Category
,TSP.SparePartDescription AS Part
,TSP.SparePartName AS PartNumber
,SP.Inventory
,Gambio.getGoodsIn(SP.idSparePart,#FromDate,#ToDate) GoodsIn
,Gambio.getOrdered(SP.idSparePart,#FromDate,#ToDate) Ordered
--,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
-- Gambio.getClaimed(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus,NULL)END AS Claimed
,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
Gambio.getClaimed(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus,1)END AS ClaimedReused
,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
Gambio.getCostSaving(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus)END AS Costsaving
FROM Gambio.SparePart AS SP
INNER JOIN tabSparePart AS TSP ON SP.fiTabSparePart = TSP.idSparePart
INNER JOIN tabSparePartCategory AS Cat
ON Cat.idSparePartCategory=TSP.fiSparePartCategory
WHERE Cat.idSparePartCategory NOT IN(SELECT ID FROM ExclusionCat)
AND (#idSparePartCategories IS NULL
OR TSP.fiSparePartCategory IN(
SELECT Item From dbo.Split(#idSparePartCategories,',')
)
)
AND (#idSpareParts IS NULL
OR TSP.idSparePart IN(
SELECT Item From dbo.Split(#idSpareParts,',')
)
)
)
SELECT Category
--, Part
--, PartNumber
, SUM(Inventory)As InventoryCount
, SUM(GoodsIn) As GoodsIn
, SUM(Ordered) As Ordered
--, SUM(Claimed) As Claimed
, SUM(ClaimedReused)AS ClaimedReused
, SUM(Costsaving) As Costsaving
, Count(*) AS PartCount
FROM Report
GROUP BY Category
)
Solution:
Thanks to Aliostad i've solved it by first grouping and then joining(actual TVF, reduced to a minimum):
WITH Report AS(
SELECT Cat.SparePartCategoryName AS Category
,TSP.SparePartDescription AS Part
,TSP.SparePartName AS PartNumber
,SP.Inventory
,SP.GoodsIn
,SP.Ordered
,Gambio.getClaimed(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus,1) AS ClaimedReused
,Gambio.getCostSaving(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus) AS Costsaving
FROM (
SELECT GSP.fiTabSparePart
,SUM(GSP.Inventory)AS Inventory
,SUM(Gambio.getGoodsIn(GSP.idSparePart,#FromDate,#ToDate))AS GoodsIn
,SUM(Gambio.getOrdered(GSP.idSparePart,#FromDate,#ToDate))AS Ordered
FROM Gambio.SparePart GSP
GROUP BY GSP.fiTabSparePart
)As SP
INNER JOIN tabSparePart TSP ON SP.fiTabSparePart = TSP.idSparePart
INNER JOIN tabSparePartCategory AS Cat
ON Cat.idSparePartCategory=TSP.fiSparePartCategory
)
SELECT Category
, SUM(Inventory)As InventoryCount
, SUM(GoodsIn) As GoodsIn
, SUM(Ordered) As Ordered
, SUM(ClaimedReused)AS ClaimedReused
, SUM(Costsaving) As Costsaving
, Count(*) AS PartCount
FROM Report
GROUP BY Category
You are JOINing first and then GROUPing by. You need to reverse it, GROUP BY first and then JOIN.
So here in my subquery, I group by first and then join:
select
claimed,
ordered
from
#tsp
inner JOIN
(select
fitsp,
SUM(ordered) as ordered
from
#sp
group by
fitsp) as SUMS
on
SUMS.fiTsp = id;
I think you just need to select Claimed and add it to the Group By in order to get what you are looking for.
WITH cte AS(
select tsp.id As TspID,tsp.name as TspName,tsp.claimed As Claimed
,sp.id As SpID,sp.name As SpName,sp.ordered As Ordered
from #sp sp inner join #tsp tsp
on sp.fiTsp=tsp.id )
SELECT TspName, Claimed, Sum(Ordered) As Ordered
FROM cte
Group By TspName, Claimed
Your cte is an inner join between tsp and sp, which means that the data you're querying looks like this:
SpID Ordered TspID TspName Claimed
1 30 1 1235-6044 300
2 40 1 1235-6044 300
3 50 1 1235-6044 300
4 60 2 1234-5678 400
Notice how TspID, TspName and Claimed all get repeated. Grouping by TspName means that the data gets grouped in two groups, one for 1235-6044 and one for 1234-5678. The first group has 3 rows on which to run the aggregate functions, the second group only one. That's why your sum(Claimed) will get you 300*3=900.
As Aliostad suggested, you should first group by TspID and do the sum of Ordered and then join to tsp.
No need to join, just subselect:
create table #tsp(id int, name varchar(20),claimed int);
create table #sp(id int, name varchar(20),fiTsp int,ordered int);
insert into #tsp values(1,'1235-6044',300);
insert into #tsp values(2,'1234-5678',400);
insert into #sp values(1,'1235-6044',1,30);
insert into #sp values(2,'1235-6044',1,40);
insert into #sp values(3,'1235-6044',1,50);
insert into #sp values(4,'1234-5678',2,60);
WITH cte AS(
select tsp.id As TspID,tsp.name as TspName,tsp.claimed As Claimed
,sp.id As SpID,sp.name As SpName,sp.ordered As Ordered
from #sp sp inner join #tsp tsp
on sp.fiTsp=tsp.id
)
SELECT id, name, SUM(claimed) as Claimed, (SELECT SUM(ordered) FROM #sp WHERE #sp.fiTsp = #tsp.id GROUP BY #sp.fiTsp) AS Ordered
FROM #tsp
GROUP BY id, name
drop table #tsp;
drop table #sp;
Produces:
id name Claimed Ordered
1 1235-6044 300 120
2 1234-5678 400 60
-- EDIT --
Based on the additional info, this is how I might try to split the CTE to form the data as per the example. I fully admit that Aliostad's approach may yield a cleaner query but here's an attempt (completely blind) using the subselect:
CREATE FUNCTION [Gambio].[rptReusedStatistics](
#fromDate datetime
,#toDate datetime
,#fromInvoiceDate datetime
,#toInvoiceDate datetime
,#idClaimStatus varchar(50)
,#idSparePartCategories varchar(1000)
,#idSpareParts varchar(1000)
)
RETURNS TABLE AS
RETURN(
WITH ExclusionCat AS (
SELECT idSparePartCategory AS ID From tabSparePartCategory
WHERE idSparePartCategory IN(- 3, - 1, 6, 172,168)
), ReportSP AS (
SELECT fiTabSparePart
,Inventory
,Gambio.getGoodsIn(idSparePart,#FromDate,#ToDate) GoodsIn
,Gambio.getOrdered(idSparePart,#FromDate,#ToDate) Ordered
FROM Gambio.SparePart
), ReportTSP AS (
SELECT TSP.idSparePart
,Cat.SparePartCategoryName AS Category
,TSP.SparePartDescription AS Part
,TSP.SparePartName AS PartNumber
,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
Gambio.getClaimed(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus,1)END AS ClaimedReused
,CASE WHEN TSP.idSparePart IS NULL THEN 0 ELSE
Gambio.getCostSaving(TSP.idSparePart,#FromInvoiceDate,#ToInvoiceDate,#idClaimStatus)END AS Costsaving
FROM tabSparePart AS TSP
INNER JOIN tabSparePartCategory AS Cat
ON Cat.idSparePartCategory=TSP.fiSparePartCategory
WHERE Cat.idSparePartCategory NOT IN(SELECT ID FROM ExclusionCat)
AND (#idSparePartCategories IS NULL
OR TSP.fiSparePartCategory IN(
SELECT Item From dbo.Split(#idSparePartCategories,',')
)
)
AND (#idSpareParts IS NULL
OR TSP.idSparePart IN(
SELECT Item From dbo.Split(#idSpareParts,',')
)
)
)
SELECT Category
--, Part
--, PartNumber
, (SELECT SUM(Inventory) FROM ReportSP WHERE ReportSP.fiTabSparePart = idSparePart GROUP BY fiTabSparePart) AS Inventory
, (SELECT SUM(GoodsIn) FROM ReportSP WHERE ReportSP.fiTabSparePart = idSparePart GROUP BY fiTabSparePart) AS GoodsIn
, (SELECT SUM(Ordered) FROM ReportSP WHERE ReportSP.fiTabSparePart = idSparePart GROUP BY fiTabSparePart) AS Ordered
, Claimed
, ClaimedReused
, Costsaving
, Count(*) AS PartCount
FROM ReportTSP
GROUP BY Category
)
Without a better understanding of the whole schema it's difficult to cover for all the eventualities but whether this works or not (I suspect PartCount will be 1 for all instances) hopefully it'll give you some fresh thoughts for alternate approaches.
SELECT
tsp.name
,max(tsp.claimed) as claimed
,sum(sp.ordered) as ordered
from #sp sp
inner join #tsp tsp
on sp.fiTsp=tsp.id
GROUP BY tsp.name