How to Format SQL Query? Use Pivot? - sql-server

Here is my Query. The results that I get are correct but I'm having trouble getting it in the desired format. I've tried to use Pivot, but I get errors. Any ideas?
Query:
DECLARE #SMonth DATETIME
SET #SMonth = '12/01/2015'
SELECT
SMonth 'Sales Month',
c.CustNumber 'Customer',
b.Description 'Brand',
Sum (SaleQuantity) 'Qty'
FROM
DistStructure.Customer c
JOIN Sales.Sale s ON s.CustId = c.CustId
JOIN Sales.Import i on i.ImportRefId = s.ImportRefId
JOIN AppSecurity.Log l on l.LogId = s.ImportRefId
JOIN Sales.Prod p on p.ProdId = s.ProdId
JOIN Sales.Brand b on b.BrandId = p.BrandId
WHERE
s.SMonth = #SMonth AND
i.ImportStatId = 50
Group By
CustNumber,
SMonth,
Description
Order By
CustNumber
Query Results:
Sales Month Customer Brand Qty
----------------------------------------------------
2015-12-01 00:00:00.000 030554 FS 29
2015-12-01 00:00:00.000 030554 BS 5
2015-12-01 00:00:00.000 032204 FZ 21
2015-12-01 00:00:00.000 032204 BS 14
2015-12-01 00:00:00.000 032204 FS 114
2015-12-01 00:00:00.000 034312 FZ 8
2015-12-01 00:00:00.000 034312 FS 104
2015-12-01 00:00:00.000 034312 BS 16
2015-12-01 00:00:00.000 034983 FS 63
2015-12-01 00:00:00.000 034983 BS 18
2015-12-01 00:00:00.000 034983 FZ 3
Desired Format:
Note: The Customer should be rolled up by Brand (so there is only one row per Customer) and then totaled. If the Brand has no data a zero should be placed in the spot.
Sales Month Customer BS FS FZ Total
--------------------------------------------------------------
2015-12-01 00:00:00.000 030554 5 29 0 34
2015-12-01 00:00:00.000 032204 14 114 21 149
2015-12-01 00:00:00.000 034312 16 104 8 128
2015-12-01 00:00:00.000 034983 18 63 3 84

Here is one way using Conditional Aggregate to alter your existing query to get the desired result format.
;with cte as
(
SELECT [Sales Month]=SMonth,
[Customer]= c.CustNumber,
[BS] = Sum(CASE WHEN b.Description = 'BS' THEN SaleQuantity ELSE 0 END),
[FS]= Sum(CASE WHEN b.Description = 'FS' THEN SaleQuantity ELSE 0 END),
[FZ]= Sum(CASE WHEN b.Description = 'FZ' THEN SaleQuantity ELSE 0 END)
FROM DistStructure.Customer c
JOIN Sales.Sale s
ON s.CustId = c.CustId
JOIN Sales.Import i
ON i.ImportRefId = s.ImportRefId
JOIN AppSecurity.Log l
ON l.LogId = s.ImportRefId
JOIN Sales.Prod p
ON p.ProdId = s.ProdId
JOIN Sales.Brand b
ON b.BrandId = p.BrandId
WHERE s.SMonth = #SMonth
AND i.ImportStatId = 50
GROUP BY CustNumber,
SMonth
ORDER BY [Customer]
)
SELECT [Sales Month],
[Customer],
[BS],
[FS],
[FZ],
TOTAL=[BS] + [FS] + [FZ]
FROM CTE
Note: If number of Brand's are unknown then you need to use dynamic code

I believe this is what you are looking for:
/*
Setup Sample Table
*/
declare #t table
(
[Sales Month] datetime,
Customer nvarchar(6),
Brand nvarchar(2),
Qty tinyint
)
/*
Setup Sample Table with
*/
insert into #t
([Sales Month], Customer, Brand, Qty)
values ('2015-12-01', '030554', N'FS', 29),
('2015-12-01', '030554', N'BS', 5),
('2015-12-01', '032204', N'FZ', 21),
('2015-12-01', '032204', N'BS', 14),
('2015-12-01', '032204', N'FS', 114),
('2015-12-01', '034312', N'FZ', 8),
('2015-12-01', '034312', N'FS', 104),
('2015-12-01', '034312', N'BS', 16),
('2015-12-01', '034983', N'FS', 63),
('2015-12-01', '034983', N'BS', 18),
('2015-12-01', '034983', N'FZ', 3)
/*
Generating desired output
*/
select pvt.[Sales Month],
pvt.Customer,
isnull(pvt.BS, 0) as BS,
isnull(pvt.FS, 0) as FS,
isnull(pvt.FZ, 0) as FZ,
isnull(pvt.BS, 0) + isnull(pvt.FS, 0) + isnull(pvt.FZ, 0) as Total
from #t as t pivot
( sum(Qty) for Brand in (BS, FS, FZ) ) as pvt

Related

Snowflake: window function 'range' not support, how to query this?

I have a table of transactions that includes txn_date and cust_id.
For each customer that had a transaction in December, I want to know how many transactions that customer had in the 90 days previous to the given transaction.
This seems to be a query that I could run with a window function and a RANGE sliding window, but Snowflake doesn't support the RANGE sliding window frame.
How can I run this query in Snowflake?
How about something like this:
WITH T1 AS (
SELECT CUSTOMER_ID, TX_DATE
FROM TRANSACTIONS
WHERE TX_DATE BETWEEN '2020-12-01' AND '2020-12-31')
SELECT T2.CUSTOMER_ID, T2.TX_DATE
FROM TRANSACTIONS T2
INNER JOIN T1 ON T2.CUSTOMER_ID = T2.CUSTOMER_ID
WHERE T2.TX_DATE BETWEEN (T1.TX_DATE - 90) AND T1.TX_DATE
So much the same is NickW's answer at first.
WITH data AS (
SELECT txn_date::timestamp_ntz as txn_date, cust_id, txn_id
FROM VALUES
('2020-12-04',0, 0),
('2020-12-03',1, 1),
('2020-11-04',1, 2),
('2020-10-04',1, 3),
('2020-09-04',1, 4), -- just on 90 days
('2020-09-02',1, 5), -- too far
('2021-01-05',1, 6) -- in the future
v(txn_date , cust_id, txn_id)
), dec_txn AS (
SELECT txn_id,
cust_id,
DATEADD('day',-90, txn_date) AS win_start,
txn_date AS win_end
FROM data
WHERE date_trunc('month', txn_date) = '2020-12-01'
)
SELECT dt.*
,t.*
,datediff('days', dt.win_end, t.txn_date) as win_time
FROM dec_txn AS dt
LEFT JOIN data AS t
ON t.cust_id = dt.cust_id
AND t.txn_date between dt.win_start and win_end AND t.txn_id != dt.txn_id
;
which gives:
TXN_ID CUST_ID WIN_START WIN_END TXN_DATE CUST_ID TXN_ID WIN_TIME
1 1 2020-09-04 00:00:00.000 2020-12-03 00:00:00.000 2020-11-04 00:00:00.000 1 2 -29
1 1 2020-09-04 00:00:00.000 2020-12-03 00:00:00.000 2020-10-04 00:00:00.000 1 3 -60
1 1 2020-09-04 00:00:00.000 2020-12-03 00:00:00.000 2020-09-04 00:00:00.000 1 4 -90
0 0 2020-09-05 00:00:00.000 2020-12-04 00:00:00.000 NULL NULL NULL NULL
thus to counts we:
WITH data AS (
SELECT txn_date::timestamp_ntz as txn_date, cust_id, txn_id
FROM VALUES
('2020-12-04',0, 0),
('2020-12-03',1, 1),
('2020-11-04',1, 2),
('2020-10-04',1, 3),
('2020-09-04',1, 4), -- just on 90 days
('2020-09-02',1, 5), -- too far
('2021-01-05',1, 6) -- in the future
v(txn_date , cust_id, txn_id)
), dec_txn AS (
SELECT txn_id,
cust_id,
txn_date,
DATEADD('day',-90, txn_date) AS win_start,
txn_date AS win_end
FROM data
WHERE date_trunc('month', txn_date) = '2020-12-01'
)
SELECT dt.cust_id
,dt.txn_id
,dt.txn_date
,count(t.txn_id) as c__prior_90_days_transaction
FROM dec_txn AS dt
LEFT JOIN data AS t
ON t.cust_id = dt.cust_id
AND t.txn_date >= dt.win_start and t.txn_date < dt.win_end AND t.txn_id != dt.txn_id
GROUP BY 1,2,3
ORDER BY 1,2
;
giving:
CUST_ID TXN_ID TXN_DATE C__PRIOR_90_DAYS_TRANSACTION
0 0 2020-12-04 00:00:00.000 0
1 1 2020-12-03 00:00:00.000 3
What is not well defined in the question is what to do if there are many requests in december for one customer
What to do if there are multiple transactions in the same december day.
The above will return a row for each Dec transaction per customer, and it includes transactions that happen on the same day. But if you date/timestamp has time then it will only count transtions earlier in the same day.
But if you want prior days and the txn_date is just a date then
AND t.txn_date >= dt.win_start and t.txn_date < dt.win_end AND t.txn_id != dt.txn_id
should be used.
if txn_date is a timestamp, then dec_txn should be altered to:
dec_txn AS (
SELECT txn_id,
cust_id,
DATEADD('day',-90, txn_date::date) AS win_start,
txn_date::date AS win_end
FROM data
WHERE date_trunc('month', txn_date) = '2020-12-01'
and now that the window timestamps are truncated to days, then you will have to workout if you want midnight transaction to count on the day, or if you don't have midnight timestamps...

SQL - Display Months as columns

I have a SQL Server table that has Start (1-1-2017) and End (1-1-2022) dates for contracts with invoices being generated each month for current and past months.
I would like to display months as columns even when no invoice has been generated, is that possible with just SQL / Pivot tables or a table with dates as calendar must be created?
I have worked with this code so far.
WITH CTE_MyTable AS
(
SELECT
FORMAT(MIN(StartDate), 'yyyy-MM') AS [MyDate]
FROM
MyTable
UNION ALL
SELECT
FORMAT(MIN(DATEADD(month, 1, StartDate)), 'yyyy-MM') AS [MyDate]
FROM
MyTable
WHERE
FORMAT(DATEADD(month, 1, StartDate),'yyyy-MM') <= (SELECT FORMAT(MAX(EndDate), 'yyyy-MM') AS [MyDate] FROM MyTable)
)
SELECT [MyDate]
FROM CTE_ MyTable
GROUP BY MyDate
OPTION (MAXRECURSION 0);
So let's say your table looked like this (using temp variable so you can just copy/paste/test):
DECLARE #sale TABLE(saledate DATE, saleamt MONEY);
INSERT #sale VALUES ('20170103',500),('20170128',266),('20170303',4002),('20170409',25);
Note that I'm only doing 6 months for simplicity. The following query get the count of sales per month (first query) and the sum of the sales for the second query:
DECLARE #sale TABLE(saledate DATE, saleamt MONEY);
INSERT #sale VALUES ('20170103',500),('20170128',266),('20170303',4002),('20170409',25);
SELECT
[201701] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 1 THEN 1 END),
[201702] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 2 THEN 1 END),
[201703] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 3 THEN 1 END),
[201704] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 4 THEN 1 END),
[201705] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 5 THEN 1 END),
[201706] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 6 THEN 1 END)
FROM #sale t;
SELECT
[201701] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 1 THEN t.saleamt END),0),
[201702] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 2 THEN t.saleamt END),0),
[201703] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 3 THEN t.saleamt END),0),
[201704] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 4 THEN t.saleamt END),0),
[201705] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 5 THEN t.saleamt END),0),
[201706] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 6 THEN t.saleamt END),0)
FROM #sale t;
These queries return:
201701 201702 201703 201704 201705 201706
----------- ----------- ----------- ----------- ----------- -----------
2 0 1 1 0 0
201701 201702 201703 201704 201705 201706
--------------------- --------------------- --------------------- --------------------- --------------------- ---------------------
766.00 0.00 4002.00 25.00 0.00 0.00
There is a way to pivot columns in SQL- using the Pivot() function (Microsoft Documentation at: https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017)
To display months as columns (1 to 12) the Pivot() function assigns values to (hard-coded) columns. Implemented correctly, there are NULLS where the aggregation doesn't occur due to lack of records. The implied way to replace NULLS with zeroes is by using the COALESCE() function.
The year values of the pivoted data should be grouped and while there is no specific Group By for a pivot, this is implied by how the SourceTable query is written.
In this example code I use the same exact format as the official documentation; with the exception that I additionally group by year and COALESCE NULLs with 0's.
What I am doing is counting the number of records for a given month and year:
SELECT yearval
,COALESCE([1], 0) [Jan]
,COALESCE([2], 0) [Feb]
,COALESCE([3], 0) [Mar]
,COALESCE([4], 0) [Apr]
,COALESCE([5], 0) [May]
,COALESCE([6], 0) [Jun]
,COALESCE([7], 0) [Jul]
,COALESCE([8], 0) [Aug]
,COALESCE([9], 0) [Sep]
,COALESCE([10], 0) [Oct]
,COALESCE([11], 0) [Nov]
,COALESCE([12], 0) [Dec]
FROM
(SELECT YEAR([Your_Date_Column_Here]) AS [yearval]
,MONTH([Your_Date_Column_Here]) AS [monthval]
FROM [Your_Table_Name_Here]) AS SourceTable
PIVOT
(
COUNT(monthval) FOR monthval IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) AS PivotTable
Would produce this output in my test database:
yearval Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
2015 1952 1122 1678 2364 1125 1308 1414 2103 1031 1340 2506 1015
2016 1123 1413 1568 1421 1278 1252 1048 1290 1251 1571 2647 1253
2017 0 0 0 3 0 1241 2377 2714 6724 1388 1521 1243
2018 2127 2118 2449 2330 2687 3833 3279 883 0 0 0 0

Amount is not populating correctly

I have this query which I wrote to get the opening balance and balance amount but it is not calculating the CrAmount. I'm not sure if I can provide table structure and data as that's too large and complex to put here so if anyone could spot out the issue.
;WITH CTE AS (
SELECT
[Master].[TransactionCode], [Master].[TransactionDate], SUM(DrAmount) [DrAmount], SUM(CrAmount) [CrAmount]
FROM
[FICO].[tbl_TransactionDetail] [Detail], [FICO].[tbl_TransactionMaster] [Master]
--WHERE [VoucherDate] BETWEEN CAST('Dec 01 2017 12:00AM' AS DATE) AND CAST('Dec 30 2017 12:00AM' AS DATE)
WHERE
[Master].[ID] = [Detail].[TransactionCode]
GROUP BY [Detail].[ID], [Master].[TransactionCode], [Master].[TransactionDate]
)
SELECT TransactionCode, [TransactionDate], D.DrAmount, D.CrAmount, D.Amount, D.Amount-ISNULL(D.DrAmount,D.CrAmount) [Opening]
FROM(
SELECT *,
SUM(ISNULL(DrAmount, 0)+ISNULL(CrAmount, 0)) OVER (ORDER BY TransactionCode
) as Amount
FROM CTE
)D
WHERE D.[TransactionDate] BETWEEN CAST('Mar 26 2018 12:00AM' AS DATE) AND CAST('Mar 30 2018 12:00AM' AS DATE)
Output is this. Here if the DrAmount is given it will subtract it from Amount and show Opening Balance. Amount is being calculated with respect to previous entries in the database.
TransactionCode TransactionDate DrAmount CrAmount Amount Opening
-------------------------------------------------- ----------------------- --------------------------------------- --------------------------------------- --------------------------------------- ---------------------------------------
CPV--43---18 2018-03-26 00:00:00.000 25000.0000000 0.0000000 98666164.0000000 98641164.0000000
CPV--43---18 2018-03-26 00:00:00.000 0.0000000 28700.0000000 98666164.0000000 98666164.0000000
CPV--43---18 2018-03-26 00:00:00.000 1500.0000000 0.0000000 98666164.0000000 98664664.0000000
CPV--43---18 2018-03-26 00:00:00.000 2200.0000000 0.0000000 98666164.0000000 98663964.0000000
CPV--44---18 2018-03-27 00:00:00.000 2300.0000000 0.0000000 98670764.0000000 98668464.0000000
CPV--44---18 2018-03-27 00:00:00.000 0.0000000 2300.0000000 98670764.0000000 98670764.0000000
Maybe you should handle it like this:
DR = case when SUM(DR-CR) >=0 then SUM(DR-CR) else 0 end
CR = case when SUM(CR-DR) >=0 then SUM(CR-DR) else 0 end
Here's how to apply this:
--put in your dates here
declare #start date = '20180301'
, #end date = '20180331'
select transactionid
,DR = case when SUM(DR-CR) >=0 then SUM(DR-CR) else 0 end
,CR = case when SUM(CR-DR) >=0 then SUM(CR-DR) else 0 end
from detail d
join header h on d.id=h.id
where trandate <= #start
group by transactionID
union all
select transactionid, DR,CR
from detail
join header h on d.id=h.id
where transdate between #start and #end

13 Period Calendar 4-4-5 Calendar T-SQL MSSQL

I am trying to create a 13 period calendar in mssql but I am a bit stuck. I am not sure if my approach is the best way to achieve this. I have my base script which can be seen below:
Set DateFirst 1
Declare #Date1 date = '20180101' --startdate should always be start of
financial year
Declare #Date2 date = '20181231' --enddate should always be start of
financial year
SELECT * INTO #CalendarTable
FROM dbo.CalendarTable(#Date1,#Date2,0,0,0)c
DECLARE #StartDate datetime,#EndDate datetime
SELECT #StartDate=MIN(CASE WHEN [Day]='Monday' THEN [Date] ELSE NULL END),
#EndDate=MAX([Date])
FROM #CalendarTable
;With Period_CTE(PeriodNo,Start,[End])
AS
(SELECT 1,#StartDate,DATEADD(wk,4,#StartDate) -1
UNION ALL
SELECT PeriodNo+1,DATEADD(wk,4,Start),DATEADD(wk,4,[End])
FROM Period_CTE
WHERE DATEADD(wk,4,[End])< =#EndDate
OR PeriodNo+1 <=13
)
select * from Period_CTE
Which gives me this:
PeriodNo Start End
1 2018-01-01 00:00:00.000 2018-01-28 00:00:00.000
2 2018-01-29 00:00:00.000 2018-02-25 00:00:00.000
3 2018-02-26 00:00:00.000 2018-03-25 00:00:00.000
4 2018-03-26 00:00:00.000 2018-04-22 00:00:00.000
5 2018-04-23 00:00:00.000 2018-05-20 00:00:00.000
6 2018-05-21 00:00:00.000 2018-06-17 00:00:00.000
7 2018-06-18 00:00:00.000 2018-07-15 00:00:00.000
8 2018-07-16 00:00:00.000 2018-08-12 00:00:00.000
9 2018-08-13 00:00:00.000 2018-09-09 00:00:00.000
10 2018-09-10 00:00:00.000 2018-10-07 00:00:00.000
11 2018-10-08 00:00:00.000 2018-11-04 00:00:00.000
12 2018-11-05 00:00:00.000 2018-12-02 00:00:00.000
13 2018-12-03 00:00:00.000 2018-12-30 00:00:00.000
The result i am trying to get is
Even if I have to take a different approach I would not mind, as long as the result is the same as the above.
dbo.CalendarTable() is a function that returns the following results. I can share the code if desired.
I'd create a general number's table like suggested here and add a column Periode13.
The trick to get the tiling is the integer division:
DECLARE #PeriodeSize INT=28; --13 "moon-months" a 28 days
SELECT TOP 100 (ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1)/#PeriodeSize
FROM master..spt_values --just a table with many rows to show the principles
You can add this to an existing numbers table with a simple update statement.
UPDATE A fully working example (using the logic linked above)
DECLARE #RunningNumbers TABLE (Number INT NOT NULL
,CalendarDate DATE NOT NULL
,CalendarYear INT NOT NULL
,CalendarMonth INT NOT NULL
,CalendarDay INT NOT NULL
,CalendarWeek INT NOT NULL
,CalendarYearDay INT NOT NULL
,CalendarWeekDay INT NOT NULL);
DECLARE #CountEntries INT = 100000;
DECLARE #StartNumber INT = 0;
WITH E1(N) AS(SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)), --10 ^ 1
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b), -- 10 ^ 8 = 10,000,000 rows
CteTally AS
(
SELECT TOP(ISNULL(#CountEntries,1000000)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1 + ISNULL(#StartNumber,0) As Nmbr
FROM E8
)
INSERT INTO #RunningNumbers
SELECT CteTally.Nmbr,CalendarDate.d,CalendarExt.*
FROM CteTally
CROSS APPLY
(
SELECT DATEADD(DAY,CteTally.Nmbr,{ts'2018-01-01 00:00:00'})
) AS CalendarDate(d)
CROSS APPLY
(
SELECT YEAR(CalendarDate.d) AS CalendarYear
,MONTH(CalendarDate.d) AS CalendarMonth
,DAY(CalendarDate.d) AS CalendarDay
,DATEPART(WEEK,CalendarDate.d) AS CalendarWeek
,DATEPART(DAYOFYEAR,CalendarDate.d) AS CalendarYearDay
,DATEPART(WEEKDAY,CalendarDate.d) AS CalendarWeekDay
) AS CalendarExt;
--The mockup table from above is now filled and can be queried
WITH AddPeriode AS
(
SELECT Number/28 +1 AS PeriodNumber
,CalendarDate
,CalendarWeek
,r.CalendarDay
,r.CalendarMonth
,r.CalendarWeekDay
,r.CalendarYear
,r.CalendarYearDay
FROM #RunningNumbers AS r
)
SELECT TOP 100 p.*
,(SELECT MIN(CalendarDate) FROM AddPeriode AS x WHERE x.PeriodNumber=p.PeriodNumber) AS [Start]
,(SELECT MAX(CalendarDate) FROM AddPeriode AS x WHERE x.PeriodNumber=p.PeriodNumber) AS [End]
,(SELECT MIN(CalendarDate) FROM AddPeriode AS x WHERE x.PeriodNumber=p.PeriodNumber AND x.CalendarWeek=p.CalendarWeek) AS [wkStart]
,(SELECT MAX(CalendarDate) FROM AddPeriode AS x WHERE x.PeriodNumber=p.PeriodNumber AND x.CalendarWeek=p.CalendarWeek) AS [wkEnd]
,(ROW_NUMBER() OVER(PARTITION BY PeriodNumber ORDER BY CalendarDate)-1)/7+1 AS WeekOfPeriode
FROM AddPeriode AS p
ORDER BY CalendarDate
Try it out...
Hint: Do not use a VIEW or iTVF for this.
This is non-changing data and much better placed in a physically stored table with appropriate indexes.
Not abundantly sure external links are accepted here, but I wrote an article that pulls of a 5-4-4 'Crop Year' fiscal year with all the code. Feel free to use all the code in these articles.
SQL Server Calendar Table
SQL Server Calendar Table: Fiscal Years

Group by month and year of a date column

I have my query as following
SELECT
MAX(Reimbursement_EBSUtilization.Id) AS Id,
ProviderReimbursementRequest.Contractor_Id,
Reimbursement_EBSUtilization.ServiceMonth,
fContractor.ContractorName,
Reimbursement_EBSUtilization.SD_Id,
MAX(StandardUnits) AS StandardUnits,
MAX(Rate) AS Rate,
SUM(Reimbursement_EBSUtilization.UnitsDelivered) AS UnitsDelivered,
NULL AS ReduceUnits,
CAST(1 AS bit) AS IsEbs,
Reimbursement_EBSUtilization.BHFormName,
fExpenseType.ExpenseType,
CASE
WHEN Reimbursement_EBSUtilization.BHFormName IS NULL THEN MAX(Rate) * SUM(Reimbursement_EBSUtilization.UnitsDelivered) * ISNULL(MAX(Reimbursement_EBSUtilization.StandardUnits), 0)
ELSE (CASE
WHEN fExpenseType.ExpenseType = 'Payable' THEN SUM(ISNULL(Reimbursement_BHForms.ReimburseAmount, 0)) - SUM(ISNULL(Reimbursement_BHForms.ReducedAmount, 0))
ELSE 0
END) -
(CASE
WHEN fExpenseType.ExpenseType = 'Offset' THEN SUM(ISNULL(Reimbursement_BHForms.ReimburseAmount, 0)) - SUM(ISNULL(Reimbursement_BHForms.ReducedAmount, 0))
ELSE 0
END)
END AS ReimbursementAmount
FROM
ProviderReimbursementRequest
LEFT JOIN
Reimbursement_EBSUtilization ON ProviderReimbursementRequest.Id = Reimbursement_EBSUtilization.PRR_Id
LEFT JOIN
Reimbursement_BHForms ON Reimbursement_EBSUtilization.Id = Reimbursement_BHForms.REU_Id
LEFT JOIN
fExpenseCategory ON Reimbursement_BHForms.EC_Id = fExpenseCategory.ID
LEFT JOIN
fExpenseType ON fExpenseCategory.ExpenseType = fExpenseType.Id
LEFT JOIN
fContractor ON ProviderReimbursementRequest.Contractor_Id = fContractor.Id
WHERE
MRR_Id = #MrrId
AND Reimbursement_EBSUtilization.SD_Id = #ServiceDetailId
GROUP BY
ProviderReimbursementRequest.Contractor_Id,
Reimbursement_EBSUtilization.ServiceMonth,
fContractor.ContractorName,
Reimbursement_EBSUtilization.SD_Id,
Reimbursement_EBSUtilization.BHFormName,
fExpenseType.ExpenseType
On executing the result is
Id Contractor_Id ServiceMonth ContractorName SD_Id StandardUnits Rate UnitsDelivered ReduceUnits IsEbs BHFormName ExpenseType ReimbursementAmount
3976 845 2016-05-01 Payments SC1 2867 1.00 10.00 20 NULL 1 NULL NULL 200.00
3966 845 2016-07-31 Payments SC1 2867 1.00 10.00 NULL NULL 1 NULL NULL NULL
3974 846 2016-07-01 Payments SC2 2867 1.00 10.00 100 NULL 1 NULL NULL 1000.00
3970 846 2016-07-31 Payments SC2 2867 1.00 10.00 20 NULL 1 NULL NULL 200.00
3978 847 2016-07-31 Payments SC3 2867 1.00 10.00 30 NULL 1 NULL NULL 300.00
3983 847 2016-08-01 Payments SC3 2867 1.00 10.00 NULL NULL 1 NULL NULL NULL
If you observe the service month column for contractor_id = 846 we can see 2 records with same month.
I want the output to combine these columns as one is with 2016-07-01 and other is with 2016-07-31 as they both belong to same month and year. I want them to be combined.
Can any one help on this ?
You really should get in the habit of using aliases and formatting your queries. As posted that query is impossible to decipher. With just some aliases and a little formatting it is a lot cleaner.
select Max(ru.Id) as Id
, prr.Contractor_Id
, ru.ServiceMonth
, c.ContractorName
, ru.SD_Id
, Max(StandardUnits) as StandardUnits
, max(Rate) as Rate
, sum(ru.UnitsDelivered) as UnitsDelivered
, null as ReduceUnits
, Cast(1 as BIT) as IsEbs
, ru.BHFormName
, et.ExpenseType
, case when ru.BHFormName is null
then max(Rate) * sum(ru.UnitsDelivered) * ISNULL(max(ru.StandardUnits),0)
else
(
case when et.ExpenseType = 'Payable'
then sum(ISNULL(f.ReimburseAmount,0)) - sum(ISNULL(f.ReducedAmount,0))
else 0
end
) -
(
case when et.ExpenseType = 'Offset'
then sum(ISNULL(f.ReimburseAmount,0)) - sum(ISNULL(f.ReducedAmount,0))
else 0
end
) end as ReimbursementAmount
from ProviderReimbursementRequest prr
left join Reimbursement_EBSUtilization ru on prr.Id = ru.PRR_Id
left join Reimbursement_BHForms f on ru.Id = f.REU_Id
left join fExpenseCategory ec on f.EC_Id = ec.ID
left join fExpenseType et on ec.ExpenseType = et.Id
left join fContractor c on prr.Contractor_Id = c.Id
where MRR_Id = #MrrId
and ru.SD_Id = #ServiceDetailId
group by prr.Contractor_Id
, ru.ServiceMonth
, c.ContractorName
, ru.SD_Id
, ru.BHFormName
, et.ExpenseType
To actually help with your problem I think we need a bit more detail. This is a great place to start. http://spaghettidba.com/2015/04/24/how-to-post-a-t-sql-question-on-a-public-forum/
--EDIT--
If I understand the issue you need to group by the first of the month in ru.ServiceMonth instead of the actual value.
Something like this.
dateadd(month, datediff(month, 0, ru.ServiceMonth), 0)

Resources