SSRS Recursive Sum - sql

I have created a tablet report in vs2017 see here. Each balanceindicator has a parent key. For my "Sum at the end of last year" field i have configure a recursive sum by parentKey for BalanceIndicatorKey group. At this point all is good and works perfectly. Now i have added a column group by organizationKey. What i need is to get the recursive sum for each organization considering the Organization and balance indicator My report
Here is the stored procedure that i use for my report
LTER PROCEDURE [dbo].[GetFactBalance]
#organizationKey int,
#year int,
#month int
AS
BEGIN
SET NOCOUNT ON;
;with cte_Orgs
as
(
select OrganizationKey,
[Description]
from DimOrganization o
where o.OrganizationKey = #organizationKey
union all
select o.OrganizationKey,
o.[Description]
from cte_Orgs join
DimOrganization o on o.ParentKey = cte_Orgs.OrganizationKey
),
cte_start as (
select
Sum(fa.SumReg) SumReg,
fa.BalanceIndicatorKey,
cte_Orgs.OrganizationKey,
cte_Orgs.Description
from FactBalance fa join
cte_Orgs on cte_Orgs.OrganizationKey = fa.OrganizationKey
where fa.OrganizationKey = cte_Orgs.OrganizationKey
and [Year] = #year-1 and [Month] = 12
group by
fa.BalanceIndicatorKey,
cte_Orgs.OrganizationKey,
cte_Orgs.Description
),
cte_curr as (
select
Sum(fa.SumReg) SumReg,
fa.BalanceIndicatorKey,
cte_Orgs.OrganizationKey,
cte_Orgs.Description
from FactBalance fa join
cte_Orgs on cte_Orgs.OrganizationKey = fa.OrganizationKey
where [Year] = #year and [Month] = #month
group by
fa.BalanceIndicatorKey,
cte_Orgs.OrganizationKey,
cte_Orgs.Description
),
cte_res as (
select
ISNULL(cte_curr.BalanceIndicatorKey, cte_start.BalanceIndicatorKey) BalanceIndicatorKey,
ISNULL(cte_start.OrganizationKey, cte_curr.OrganizationKey) OrganizationKey,
ISNULL(cte_start.Description, cte_curr.Description) [OrgName],
cte_start.SumReg SumRegStart,
cte_curr.SumReg SumReg
from cte_curr full join cte_start on cte_curr.BalanceIndicatorKey = cte_start.BalanceIndicatorKey
and cte_curr.OrganizationKey = cte_start.OrganizationKey)
select
cte_res.OrganizationKey,
cte_res.OrgName,
bi.BalanceIndicatorKey,
bi.Description [Description],
bi.Code,
bi.ParentKey,
cte_res.SumRegStart,
cte_res.SumReg,
isnull(bi.Level, 0) [Level]
from DimBalanceIndicator bi
full join cte_res on cte_res.BalanceIndicatorKey = bi.BalanceIndicatorKey
END
Any ideas? Thank you

Related

How to remove duplicates in CTE from VARIABLES and TEMPORARY TABLE

I was looking for this solution in internet and none of them worked properly.
I decided to create variables, then accommodates it in the user defined function:
CREATE FUNCTION Top10CustomerByCategoryInYear
(
#Category varchar(MAX),
#StartYear int,
#EndYear int
)
RETURNS #CustomerTop10TableByCategoryInYears TABLE
(
[Customer Name] VARCHAR(MAX),
[Category] VARCHAR(MAX),
Sales int,
Year int
)
AS
BEGIN
INSERT INTO #CustomerTop10TableByCategoryInYears
SELECT
DISTINCT TOP 10 WITH TIES
[Customer Name],
[Category],
SUM(Sales) OVER (PARTITION BY [Customer Name]) as Sales,
YearOfOrderDate as [Year]
FROM [Project4].[dbo].[SuperStore]
WHERE [Category] = #Category AND YearOfOrderDate BETWEEN #StartYear AND #EndYear
ORDER BY Sales DESC
RETURN;
END
GO
It works properly, because for example I am able to return the top 10 Customer in the Furniture Category in 2011 and 2012
SELECT *
FROM Top10CustomerByCategoryInYear('Furniture',2011,2011)
SELECT *
FROM Top10CustomerByCategoryInYear('Furniture',2012,2012)
Then, I would like to have all records (2011-2014) in one Table. I decided to use CTE, then JOIN these Tables by Category:
WITH Furniture2011 AS (
SELECT DISTINCT *
FROM Top10CustomerByCategoryInYear('Furniture',2011,2011)
), Furniture2012 AS (
SELECT DISTINCT *
FROM Top10CustomerByCategoryInYear('Furniture',2012,2012)
), Furniture2013 AS (
SELECT DISTINCT *
FROM Top10CustomerByCategoryInYear('Furniture',2013,2013)
), Furniture2014 AS (
SELECT DISTINCT *
FROM Top10CustomerByCategoryInYear('Furniture',2014,2014)
)
SELECT DISTINCT *
FROM Furniture2011 F1
JOIN Furniture2012 F2
ON F1.Category = F2.Category
JOIN Furniture2013 F3
ON F1.Category = F3.Category
JOIN Furniture2014 F4
ON F1.Category = F4.Category
Unfortunately there are a lot of duplicates:
[
I tried a lot of solutions from my mind and internet and unfortunately these did not work.
I was wondering if it is possible to combine these Tables using UDF/Variables, TEMP TABLE and finally CTE.
Could you please tell me if it is possible?
If so, would you be so kind as to support me in modifying my codes in SQL to have the desired result without any duplicates?
I would appreciate it if you could advise me on it.
You can have an inline Table function taking a start and end year. Inline is faster than multi-statement.
Start with a virtual table of years, then CROSS APPLY the top 10 results for each year.
CREATE FUNCTION Top10CustomerByCategoryInYear
(
#Category varchar(MAX),
#StartYear int,
#EndYear int
)
RETURNS TABLE
AS RETURN
WITH L0 AS (
SELECT #StartYear - 1 + ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS Year
FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
(1),(1),(1),(1),(1),(1),(1),(1),
(1),(1),(1),(1),(1),(1),(1),(1),
(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c)
),
Years AS (
SELECT * FROM L0 WHERE Year <= #EndYear
)
SELECT
s.[Customer Name],
#Category AS [Category],
s.Sales,
y.Year
FROM Years y
CROSS APPLY (
SELECT TOP 10 WITH TIES
[Customer Name],
SUM(Sales) AS Sales
FROM [dbo].[SuperStore] s
WHERE [Category] = #Category
AND y.Year = s.YearOfOrderDate
GROUP BY [Customer Name]
ORDER BY Sales DESC
) s;
GO

Invoice creation in sql - create a stored procedure

I want to create a procedure in which the following details should be displayed:-
I have created this query but I am not getting the right results. I have attached the table schema.
TABLES WE ARE USING:- InvoiceData, CustomerDetails and InvoiceItems
Table Schema ->
1. InvoiceItems TABLE1
2. CustomerDetails Table2
3. InvoiceDetails enter image description here
There are 2 sections for invoice:-
In the first section of the Invoice, Below details should be displayed.
Invoice Information section
In the second section of the invoice, the below details should be displayed:-
Invoice Items description section
I am attaching the query below:-
alter Procedure SaveInvoiceDetails
(
#CustomerId varchar(50),
#InvoiceNumber varchar(50),
#InvoiceDate date,
#InvoiceMonth int,
#FromDate date,
#ToDate date,
#Rate int,
#Quantity int,
#ActualAmount int,
#ZoneId int
)
as
set nocount on;
begin
Declare #TotalRows int
Declare #NumPages int
set #TotalRows = 0
Select ROW_NUMBER() over (order by C.CustomerId) as InvoiceRow,
C.CustomerId, I.InvoiceNumber, I.InvoiceDate, I.FromDate, I.ToDate,
I.InvoiceMonth, I.Rate, I.ActualAmount, I.Quantity, C.ZoneId,
C.BillingAmount
into #tempInvoice
from ConsumerMST_LKO C
inner join InvoiceDetails I
on C.CustomerId = I.CustomerId
inner join INVOICEITEMS II
on I.InvoiceNumber = II.INVOICEID
where InvoiceNumber = #InvoiceNumber AND InvoiceDate = #InvoiceDate AND
InvoiceMonth = #InvoiceMonth
AND FromDate =#FromDate AND ToDate = #ToDate AND ActualAmount =
#ActualAmount
set #TotalRows = ##ROWCOUNT
If #TotalRows = 0
Begin
set #TotalRows = #TotalRows + 1
Insert #tempInvoice
(
InvoiceNumber,
InvoiceDate,
InvoiceMonth,
ZoneId,
Rate,
Quantity,
BillingAmount,
FromDate,
ToDate
)
VALUES
(#TotalRows
, ''
,''
,''
,0
,0
,0
,0
,''
,0)
End
End
SELECT * FROM #tempInvoice ORDER BY InvoiceRow asc
return
So I am expecting that you got all the records in your #tempInvoice from your procedure. And face problem in only generating invoice number with the format you provided.
For each region you already have a specific code generated at your end ( I am guessing).
#regioncode ---- this table contain record of zoneid and zoneocde for each area
If you don't have any table for region then prepare one before going forward as this needs you everywhere in your code section.
At the end pf your procedure need to update this to return your required result.
; with cte as (
select row_number() over (partition by ZoneId order by InvoiceDate) as Slno, * from #tempInvoice as t inner join #regioncode as r on t.zoneid=r.zoneid
)
select zonecode + '_' + cast(slno as varchar(10)) as Uniquecode, * from cte

Understanding a SQL Statement

I'm trying to modify a stored procedure on a Microsoft SQL server that is used to get data for a SSRS table. It currently filters by a few parameters, one of them "Analyst" but I would like for the query to return the analyst as well to the report so if you leave the parameter empty it would you could still see what analyst was assigned the ticket
This is the code used to find the analyst info, how can I edit it to allow me to return the Analyst display name as well?
LEFT OUTER JOIN(
SELECT
Analyst.UserName AS AnalystASURITE,
Analyst.DisplayName AS DisplayName,
Analyst.UserDimKey,
WIATUFact.WorkItemDimKey
FROM
dbo.UserDim Analyst
JOIN
dbo.WorkItemAssignedToUserFactvw WIATUFact
ON Analyst.UserDimKey = WIATUFact.WorkItemAssignedToUser_UserDimKey
WHERE WIATUFact.DeletedDate IS NULL -- We only need the information for the last analyst assigned.
GROUP BY WIATUFact.WorkItemDimKey, Analyst.UserName, Analyst.DisplayName, Analyst.UserDimKey, WIATUFact.CreatedDate
) AssignedAnalystInfo
ON AssignedAnalystInfo.WorkItemDimKey = WI.WorkItemDimKey
I added
Analyst = AssignedAnalystInfo.DisplayName,
to the top of my procedure and it was correct syntax but got this error
Column 'AssignedAnalystInfo.DisplayName' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I can add the whole procedure if that is needed but it's pretty long.
USE [DWDataMart]
GO
/****** Object: StoredProcedure [dbo].[RTS_Report_IncidentManagement_GetIncidentMetricData2018] Script Date: 11/29/2018 2:30:26 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[RTS_Report_IncidentManagement_GetIncidentMetricData2018]
#StartDate datetime,
#EndDate datetime,
#LanguageCode nvarchar(max)= 'ENU',
#Department nvarchar(max) = '',
#Analyst nvarchar(max) = '',
#AffectedUser nvarchar(max) = '',
#DateFilter nvarchar(256) = 'CreatedOn',
#SupportGroups nvarchar(max) = -1,
#Priority nvarchar(max) = -1
AS
BEGIN
SET NOCOUNT ON
/* Adds a day to the End Date if it is set to midnight.
This is needed as the console picks midnight of the End Date which cuts off the last day.
Otherwise it simply leaves it as is.*/
If (#EndDate = cast(#EndDate as date))
SET #EndDate = DATEADD(DAY, 1, #EndDate)
DECLARE #Error int
DECLARE #ExecError int
DECLARE #tableDept TABLE (value nvarchar(256))
INSERT #tableDept (value)
SELECT * FROM dbo.fn_CSVToTableString(#Department)
DECLARE #tableAnalyst TABLE (value nvarchar(256))
INSERT #tableAnalyst(value)
SELECT * FROM dbo.fn_CSVToTableString(#Analyst)
DECLARE #tableAffectedUser TABLE (value nvarchar(256))
INSERT #tableAffectedUser(value)
SELECT * FROM dbo.fn_CSVToTableString(#AffectedUser)
DECLARE #tableSupportGroups TABLE (value nvarchar(256))
INSERT #tableSupportGroups (value)
SELECT * FROM dbo.fn_CSVToTableInt(#SupportGroups)
DECLARE #tablePriority TABLE (value nvarchar(256))
INSERT #tablePriority (value)
SELECT * FROM dbo.fn_CSVToTableInt(#Priority)
SELECT
--We need some datasets. This SP will pull each incident based on the parameters. How it is displayed
--depends on the report running this SP.
IncidentTitle = I.Title,
AffectedUser = AffectedUserInfo.DisplayName,
IncidentIR = I.Id,
AAnalyst = AssignedAnalystInfo.DisplayName,
IRCreatedDate = I.CreatedDate,
IRResolvedDate = I.ResolvedDate,
TimeToAssignment = CASE
WHEN
(
FirstAssignedDate.CreatedDate IS NOT NULL
AND
I.Priority > 3
)
THEN
DATEDIFF(MINUTE, I.CreatedDate, FirstAssignedDate.CreatedDate)
ELSE NULL
END,
TimeWorked = CASE
WHEN
(
(TotalBT.TimeWorked IS NOT NULL)
)
THEN
TotalBT.TimeWorked
ELSE NULL
END,
TimeToResolution = CASE
WHEN
(
(I.Status = 'IncidentStatusEnum.Resolved' OR I.Status = 'IncidentStatusEnum.Closed')
AND
(I.CreatedDate IS NOT NULL)
AND
(I.ResolvedDate IS NOT NULL)
AND
(I.Priority > 3)
)
THEN
DATEDIFF(MINUTE, I.CreatedDate, I.ResolvedDate) - dFact.PendingDuration
ELSE NULL
END,
-- Unseen stuff is selected and processed accordingly
IncidentDimKey = I.IncidentDimKey
FROM dbo.IncidentDim I
-- JOINS to other needed tables
-- Join the incident dimension to the workitem dimension.
INNER JOIN dbo.WorkItemDim WI
ON WI.EntityDimKey = I.EntityDimKey
--Join the AssignedTo fact table to the workitem table. We can use this to get information on the assigned
--analyst.
LEFT OUTER JOIN(
SELECT
Analyst.UserName AS AnalystASURITE,
Analyst.DisplayName AS DisplayName,
Analyst.UserDimKey,
WIATUFact.WorkItemDimKey
FROM
dbo.UserDim Analyst
JOIN
dbo.WorkItemAssignedToUserFactvw WIATUFact
ON Analyst.UserDimKey = WIATUFact.WorkItemAssignedToUser_UserDimKey
WHERE WIATUFact.DeletedDate IS NULL -- We only need the information for the last analyst assigned.
GROUP BY WIATUFact.WorkItemDimKey, Analyst.UserName, Analyst.DisplayName, Analyst.UserDimKey, WIATUFact.CreatedDate
) AssignedAnalystInfo
ON AssignedAnalystInfo.WorkItemDimKey = WI.WorkItemDimKey
--Join the Assigned To fact table so we can calculate the assignment times. Only need the first assignment information.
LEFT OUTER JOIN(
SELECT
WorkItemDimKey,
MIN(CreatedDate) AS CreatedDate
FROM
dbo.WorkItemAssignedToUserFactvw WIATUFact
GROUP BY WorkItemDimKey
) FirstAssignedDate
ON FirstAssignedDate.WorkItemDimKey = WI.WorkItemDimKey
--Start Total TimeWorked joins. We can pull time and sum per incident.
LEFT OUTER JOIN(
SELECT
SUM(BT.TimeInMinutes) AS TimeWorked,
WIBTFact.WorkItemDimKey
FROM
dbo.BillableTimeDim BT
JOIN
dbo.WorkItemHasBillableTimeFactvw WIBTFact
ON BT.BillableTimeDimKey = WIBTFact.WorkItemHasBillableTime_BillableTimeDimKey
GROUP BY WIBTFact.WorkItemDimKey
) TotalBT
ON TotalBT.WorkItemDimKey = WI.WorkItemDimKey
--Join the AffectedUser fact table to the workitem table. We need this so we have some information about
--the affeted user.
LEFT OUTER JOIN(
SELECT
UserName,
DisplayName,
Department =
CASE
WHEN(Department = '' OR Department IS NULL)
THEN 'Unknown'
ELSE Department
END,
WIAUFact.WorkItemDimKey
FROM UserDim AffectedUserInfo
JOIN dbo.WorkItemAffectedUserFactvw WIAUFact ON AffectedUserInfo.UserDimKey = WIAUFact.WorkItemAffectedUser_UserDimKey
AND
WIAUFact.DeletedDate IS NULL
GROUP BY WorkItemDimKey, CreatedDate, UserName, Department, DisplayName
) AS AffectedUserInfo
ON AffectedUserInfo.WorkItemDimKey = WI.WorkItemDimKey
--Next two JOIN needed so we can pull the name and enum values for the support groups.
LEFT OUTER JOIN dbo.IncidentTierQueues AS SupportGroupEnum
ON SupportGroupEnum.IncidentTierQueuesId = I.TierQueue_IncidentTierQueuesId
LEFT OUTER JOIN
dbo.DisplayStringDim SupportGroupDS
ON SupportGroupEnum.EnumTypeId=SupportGroupDS.BaseManagedEntityId
AND SupportGroupDS.LanguageCode = #LanguageCode
LEFT OUTER JOIN
(
SELECT
ActiveDuration = SUM(
CASE
WHEN statusEnum.ID = 'IncidentStatusEnum.Active'
THEN dFact.TotalTimeMeasure
ELSE 0
END
),
PendingDuration = SUM(
CASE
WHEN statusEnum.ID = 'IncidentStatusEnum.Active.Pending'
THEN dFact.TotalTimeMeasure
ELSE 0
END
),
dFact.IncidentDimKey
FROM
dbo.IncidentStatusDurationFactvw dFact
LEFT OUTER JOIN
dbo.IncidentStatus statusEnum
ON statusEnum.IncidentStatusId = dFact.IncidentStatusId
GROUP BY dfact.IncidentDimKey
) dFact
ON dFact.IncidentDimKey = I.IncidentDimKey
WHERE
(#StartDate <= #EndDate)
AND
I.CreatedDate >= #StartDate
AND
I.CreatedDate <= #EndDate
AND
(
(#DateFilter = 'ClosedOn' AND ((I.ClosedDate >= #StartDate) AND (I.ClosedDate < #EndDate))) OR
(#DateFilter = 'CreatedOn' AND ((I.CreatedDate >= #StartDate) AND (I.CreatedDate < #EndDate))) OR
(#DateFilter = 'ResolvedOn' AND ((I.ResolvedDate >= #StartDate) AND (I.ResolvedDate < #EndDate)))
)
AND
((-1 IN (Select value from #tableSupportGroups)) OR (CASE WHEN (SupportGroupEnum.IncidentTierQueuesId IS NULL) THEN '0' ELSE SupportGroupEnum.IncidentTierQueuesId END IN (SELECT value FROM #tableSupportGroups)))
AND
(('' IN (Select value from #tableDept)) OR (AffectedUserInfo.Department IN (Select value from #tableDept)))
AND
(('' IN (Select value from #tableAnalyst)) OR (AssignedAnalystInfo.AnalystASURITE IN(Select value from #tableAnalyst)))
AND
(('' IN (Select value from #tableAffectedUser)) OR (AffectedUserInfo.UserName IN(Select value from #tableAffectedUser)))
AND
((-1 IN (Select value from #tablePriority)) OR (I.Priority IN (Select value from #tablePriority)))
GROUP BY
I.Title,
I.Id,
I.CreatedDate,
I.ResolvedDate,
I.Priority,
I.Status,
I.IncidentDimKey,
TimeWorked,
AffectedUserInfo.DisplayName,
FirstAssignedDate.CreatedDate,
dFact.PendingDuration
SET #Error = ##ERROR
QuitError:
Return #Error
END

SQL Server 2008 R2 fails on cast varchar->time

I encountered a weird error in our SQL Server 2008 R2 server. The cast of a varchar to time fails depending on what other columns are used in SELECT clause of the top level statement. Code to reproduce the issue
CREATE FUNCTION [dbo].[explode]
(
#haystack varchar(max),
#separator varchar(8000)
)
RETURNS
#ret TABLE
(
orderCol int identity(1,1),
value varchar(max)
)
AS
BEGIN
declare #index bigint
set #index = charindex(#separator,#haystack)
while #index > 0
begin
insert into #ret (value) values (substring(#haystack,1,#index-1))
set #haystack = substring(#haystack,#index+len(#separator),len(#haystack)-#index-len(#separator)+1)
set #index = charindex(#separator,#haystack)
end
insert into #ret (value) select #haystack
RETURN
END
And the query:
declare #s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare #units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set #units = '#label,#date,#hour,'+#units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(#s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(#units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,convert(varchar,(case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end+':00'),108) as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '#label' and c2.unit = '#date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '#hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('#label','#date','#hour')
)
select unit, label, [date], value, cast([time] as time)
from topCte
This will fail with:
Msg 241, Level 16, State 1, Line 5
Conversion failed when converting date and/or time from character string.
However when I change the last two lines into any of these, it works correctly:
select unit, label, [date], value, [time]
from topCte
select unit, label, [date], cast([time] as time)
from topCte
I would like to stress that I'm fully aware that this code is sub optimal and I know how to rework this so to avoid the error by rewriting the code still fullfilling business requirement. However this error shouldn't occur in this way and I'm very curious what is triggering it.
I believe there is something wrong in how SQL Server runs the upper query.
I would store the result into a Temporary Table and then cast the result as desired
create table #table_name
(
unit varchar(3),
label varchar(10),
[date] varchar(10),
value varchar(10),
[time] varchar(10),
)
declare #s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare #units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set #units = '#label,#date,#hour,'+#units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(#s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(#units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,(case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end+':00') as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '#label' and c2.unit = '#date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '#hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('#label','#date','#hour')
)
insert into #table_name(unit, label, [date], value, [time])
select unit, label, [date], value, [time]
from topCte
select unit, label, [date], value, cast([time] as time)
from #table_name
The problem is your case expression for the [time] column. If you remove the cast in the select statement you will see values like 14:51:00 which is invalid for time. You are initially converting to a varchar but you don't specify the size which is another issue. There really is no need to convert this to a varchar because it is already a varchar value.
Here is a working version of your code.
declare #s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare #units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set #units = '#label,#date,#hour,'+#units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(#s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(#units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,
case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end
as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '#label' and c2.unit = '#date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '#hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('#label','#date','#hour')
)
select unit, label, [date], value, cast([time] as time)
from topCte

SQL Right Join?

Arrrgh, I am not getting this.
I have a table of accounts from Dynamics GP that has 7 columns. I need to fill in the blank months for accounts that didn't have any activity for a given month.
I have created an in memory table #MONTHS that has lines like so:
Account, Description, Year, Month, Month Name, Netchange, PeriodBal
1110000, NULL, 2006, 1, NULL, 0, NULL
This should match up with the same information coming from Dynamics GP. A similar line from GP would look like this:
1110000, Petty Cash, 2006, 1, January, 15.00, 343.97
If we did not spend any petty cash in February, then there would be no line for that account in 2/2006, I want to make the #MONTHS table RIGHT JOIN with the DynamicsGP table so that empty months are filled in.
Here's the abbreviated SQL shortened for readability:
SELECT Z.GPACCOUNTNO,
Z.DESCRIPTION,
Z.FISCALYEAR,
Z.FISCALPERIOD,
Z.FISCALPERIODNAME,
Z.NETCHANGE,
Z.PERIODBALANCE
FROM Z
RIGHT JOIN #MONTHS M
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
The SQL just runs forever. (i.e. 5 minutes before I lose my patience)
I have verified that my #MONTHS table looks like I intend. I have tried doing a "UNION ALL" with the two tables and it gives me duplicates.
If Table Z does not have a current line for a given account/year/month, I want my #MONTHS table to add that line with a Netchange balance of 0.
Thank you for your help. The full SQL is below.
/* Create in memory table to hold account numbers */
DECLARE #i int
DECLARE #c int
DECLARE #ACCT char(129)
DECLARE #numrows int
DECLARE #numyears int
DECLARE #y int
DECLARE #m int
DECLARE #ACCT_TABLE TABLE (
idx smallint Primary Key IDENTITY(1,1),
account char(129)
)
/* Populate account number table */
INSERT #ACCT_TABLE
select distinct ACTNUMST from SBM01.[dbo].[GL00105]
/* Year table reads available years in the DB */
DECLARE #YEAR_TABLE TABLE (
idx smallint Primary Key IDENTITY(1,1),
YEAR1 smallint
)
/* Populate year table */
INSERT #YEAR_TABLE
SELECT distinct YEAR1 FROM SBM01.dbo.SY40101 ORDER BY YEAR1
/* Create our table of months to UNION to the main accounts */
DECLARE #MONTHS table (
GPACCOUNTNO char(129),
DESCRIPTION char(51),
FISCALYEAR smallint ,
FISCALPERIOD smallint,
FISCALPERIODNAME char(21),
NETCHANGE numeric(19, 5),
PERIODBALANCE numeric(19, 5)
)
/* Here comes the heavy lifting.
We loop over the account numbers and add year and month values.
*/
SET #i = 1
SET #numrows = (SELECT COUNT(*) FROM #ACCT_TABLE)
IF #numrows > 0
WHILE(#i <= (SELECT MAX(idx) FROM #ACCT_TABLE))
BEGIN
/* Get the next account number */
SET #ACCT = (SELECT account FROM #ACCT_TABLE WHERE idx = #i)
SET #c = 1
SET #numyears = (SELECT COUNT(*) FROM #YEAR_TABLE)
WHILE(#c <= (SELECT MAX(idx) FROM #YEAR_TABLE))
BEGIN
SET #y = (SELECT YEAR1 FROM #YEAR_TABLE WHERE idx = #c)
SET #m = '0'
WHILE(#m < '13')
BEGIN
INSERT INTO #MONTHS (GPACCOUNTNO, DESCRIPTION, FISCALPERIOD, FISCALYEAR, FISCALPERIODNAME, NETCHANGE, PERIODBALANCE)
VALUES (#ACCT, NULL, #m, #y, NULL, '0', NULL)
SET #m = #m + 1
END
SET #c = #c + 1
END
SET #i = #i + 1
END
/* We should now have a populated Database */
SELECT Z.GPACCOUNTNO, Z.DESCRIPTION, Z.FISCALYEAR, Z.FISCALPERIOD, Z.FISCALPERIODNAME, Z.NETCHANGE, Z.PERIODBALANCE
FROM ( SELECT RTRIM(B.[ACTNUMST]) AS GPACCOUNTNO,
RTRIM(C.[ACTDESCR]) AS DESCRIPTION,
A.[YEAR1] AS FISCALYEAR,
A.[PERIODID] AS FISCALPERIOD,
E.[PERNAME] AS FISCALPERIODNAME,
ISNULL(A.[PERDBLNC], 0) AS NETCHANGE,
( SELECT ISNULL(SUM(D.[PERDBLNC]), 0)
FROM SBM01.[dbo].[GL10110] D
WHERE D.[ACTINDX] = A.[ACTINDX]
AND D.[YEAR1] = A.[YEAR1]
AND D.[PERIODID] <= A.[PERIODID]
) AS PERIODBALANCE
FROM SBM01.[dbo].[GL10110] A
INNER JOIN SBM01.[dbo].[GL00105] B ON B.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[GL00100] C ON C.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[SY40100] E ON E.[YEAR1] = A.[YEAR1]
AND E.[PERIODID] = A.[PERIODID]
AND E.[SERIES] = 0
UNION ALL
SELECT RTRIM(B.[ACTNUMST]) AS GPACCOUNTNO,
RTRIM(C.[ACTDESCR]) AS DESCRIPTION,
A.[YEAR1] AS FISCALYEAR,
A.[PERIODID] AS FISCALPERIOD,
E.[PERNAME] AS FISCALPERIODNAME,
ISNULL(A.[PERDBLNC], 0) AS NETCHANGE,
( SELECT ISNULL(SUM(D.[PERDBLNC]), 0)
FROM SBM01.[dbo].[GL10111] D
WHERE D.[ACTINDX] = A.[ACTINDX]
AND D.[YEAR1] = A.[YEAR1]
AND D.[PERIODID] <= A.[PERIODID]
) AS PERIODBALANCE
FROM SBM01.[dbo].[GL10111] A
INNER JOIN SBM01.[dbo].[GL00105] B ON B.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[GL00100] C ON C.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[SY40100] E ON E.[YEAR1] = A.[YEAR1]
AND E.[PERIODID] = A.[PERIODID]
AND E.[SERIES] = 0
) Z
RIGHT JOIN #MONTHS M
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
ORDER BY Z.[GPACCOUNTNO],
M.[FISCALYEAR],
M.[FISCALPERIOD]
Why don't you use the #Months table as the starting point (since it already gives you all the months you need) and fill-in the values from Z if they are available?
SELECT
M.GPACCOUNTNO,
M.DESCRIPTION,
M.FISCALYEAR,
M.FISCALPERIOD,
M.FISCALPERIODNAME,
ISNULL(Z.NETCHANGE, 0) as NETCHANGE
ISNULL(Z.PERIODBALANCE, 0) as PERIODBALANCE
FROM #MONTHS M
LEFT JOIN Z
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
You can use a SQL case statement to join when null
CREATE TABLE #TMP
(
id int,
[month] datetime
)
INSERT INTO #TMP(id,[month])values(1,GETDATE())
INSERT INTO #TMP(id,[month])values(2,null)
INSERT INTO #TMP(id,[month])values(3,GETDATE())
INSERT INTO #TMP(id,[month])values(4,GETDATE())
CREATE TABLE #TMP2
(
id int,
[month] datetime
)
INSERT INTO #TMP2(id,[month])values(1,GETDATE())
INSERT INTO #TMP2(id,[month])values(2,GETDATE())
INSERT INTO #TMP2(id,[month])values(3,GETDATE())
INSERT INTO #TMP2(id,[month])values(4,GETDATE())
select * from #TMP
select * from #TMP2
SELECT #TMP.[id], case when #TMP.[month] is null then #TMP2.[month] else #TMP.month end
from #tmp
inner join #tmp2 on #tmp.id= #tmp2.id
drop table #tmp,#tmp2