Understanding a SQL Statement - sql

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

Related

Error creating function with temporary tables

I am creating a function that contains a temporary table, it is a bit difficult for me to use the function with a temporary table, I totally do not know if it is allowed within a function since I am new to the new one.
The function I am trying to create is the following:
CREATE FUNCTION FunctionTest (
#Anio int=null,
#Mes int=Null,
#Meses int=6
)
RETURNS #Tabla TABLE (
AnioMes INT,
Viaje VARCHAR(30),
IdPorte INT,
Carga VARCHAR(20),
Peso numeric(32, 16)
)
AS
BEGIN
Declare #AnioMes varchar(8),
#AnioMes6 varchar(8)
if #Anio is null
Select #Anio = YEAR(GETDATE()),
#Mes = MONTH(GETDATE())
Select #AnioMes = (case when #Mes=12 then #Anio+1 else #Anio end *100 + Case when #Mes=12 then 1 else #Mes+1 end)*100 + 1
Select #AnioMes6 = convert(varchar(8), DATEADD(mm, -#Meses, #AnioMes), 112 )
INSERT INTO #Tabla (AnioMes,Viaje,IdPorte,Carga,Peso)
SELECT year(cpsj.Delivery)*100 + MONTH(cpsj.Delivery) as AnioMes,
tr.TId as Viaje,
cpsj.PId as IdPorte,
CASE WHEN tr.Load = 1 THEN 'CARGADO'
WHEN tr.Load = 2 THEN 'VACIO'
END as Carga,
cpsj.Weight as Peso,
into #Temp
FROM BDNEW.dbo.CENPACKSTOREJOIN cpsj
inner join TRANS tr on cpsj.ipId = tr.ipId
inner join OPERA oper on tr.OId = oper.OId
WHERE cpsj.Id = 'ID001'
AND tr.Area = 'lost'
AND tr.Status = 2
GROUP BY cpsj.Delivery, cpsj.IName
ORDER BY cpsj.ipId
if #AnioMes6 < '20160101'
insert #Temp
SELECT Year(cpsj.Delivery)*100 + MONTH(cpsj.Delivery) as AnioMes,
tr.TId as Viaje,
cpsj.PId as IdPorte,
CASE WHEN tr.Load = 1 THEN 'CARGADO'
WHEN tr.Load = 2 THEN 'VACIO'
END as Carga,
cpsj.Weight as Peso,
FROM BDOLD.dbo.CENPACKSTOREJOIN cpsj
inner join TRANS tr on cpsj.ipId = tr.ipId
inner join OPERA oper on tr.OId = oper.OId
WHERE cpsj.Id = 'ID001'
AND tr.Area = 'lost'
AND tr.Status = 2
GROUP BY cpsj.Delivery, cpsj.IName
ORDER BY cpsj.ipId
Delete #Temp
where viaje in (
select MAX(Viaje)
from #Temp
group by IdPorte
having COUNT(IdPorte) > 1
)
Select AnioMes,
Viaje,
IdPorte,
Carga,
Peso,
from #Temp
GROUP BY AnioMes,IdPorte Viaje, Carga, Peso
ORDER BY AnioMes,IdPorte
RETURN
END
If you notice I am making use of a temporary table called #Temp.
As an error message when trying to compile the function I get the following error message:
Cannot access temporary tables from within a function.
That is why I mentioned earlier if you can really make use of temporary tables in a function.
I appreciate anyone who can give me guidance on how to handle this function.
You either use
INSERT INTO #Tabla (AnioMes,Viaje,IdPorte,Carga,Peso)
SELECT year(cpsj.Delivery)*100 ...
Or
SELECT year(cpsj.Delivery)*100 ...
INTO #Temp
FROM...
Not both:
INSERT INTO #Tabla (AnioMes,Viaje,IdPorte,Carga,Peso)
SELECT year(cpsj.Delivery)*100 ...
INTO #Temp
FROM...
And also you can't use temp tables in functions.

Improve INSERT INTO SELECT Query performance?

I am trying to improve old while loop query. So far tried this but it is still slow. Not sure may be SUM perform slow.
Old query (10 mins+)
ALTER PROCEDURE [dbo].[process_tax]
#userid VARCHAR(10),
#remark NVARCHAR(500),
#tdate DATE,
#roadno NVARCHAR(10),
#inst INT
AS
BEGIN
IF OBJECT_ID('tempdb..#tempProcess_tax_1') IS NOT NULL
DROP TABLE #tempProcess_tax_1
CREATE TABLE #tempProcess_tax_1
(
RowID INT IDENTITY(1, 1),
clid_ INT,
hlid_ INT,
holdinNo_ NVARCHAR(500),
holding_ NVARCHAR(50),
clientid_ NVARCHAR(500),
clientName_ NVARCHAR(500)
)
INSERT INTO #tempProcess_tax_1 (clid_, hlid_, holdinNo_, holding_, clientid_, clientName_)
SELECT
cl.clid AS clid_, cl.id AS hlid_, holdinNo, holding,
ClientID AS clientid_, ClientName AS clientName_
FROM
tx_holding AS cl
WHERE
cl.status = 1 AND cl.roadno = #roadno
AND cl.id IN (SELECT hlid FROM tx_asset WHERE asset IS NOT NULL)
AND cl.clid IN (SELECT id FROM tbl_client WHERE client_type = 'Non-Govt.')
AND cl.id NOT IN (SELECT hlid FROM tx_bill_pay
WHERE YEAR(date_month) = YEAR(#tdate)
AND hlid IS NOT NULL
GROUP BY hlid)
DECLARE #NumberRecords_1 INT, #RowCounter_1 INT
SET #NumberRecords_1 = (SELECT COUNT(*) FROM #tempProcess_tax_1)
SET #RowCounter_1 = 1
WHILE #RowCounter_1 <= #NumberRecords_1
BEGIN
DECLARE #clid_ INT
DECLARE #hlid_ INT
DECLARE #holdinNo_ NVARCHAR(50)
DECLARE #holding_ NVARCHAR(50)
DECLARE #clientid_ NVARCHAR(100)
DECLARE #clientName_ NVARCHAR(250)
DECLARE #bill AS MONEY
DECLARE #sr AS MONEY;
SELECT
#clid_ = clid_,
#hlid_ = hlid_,
#holdinNo_ = holdinNo_, #holding_ = holding_,
#clientid_ = clientid_, #clientName_ = clientName_
FROM
#tempProcess_tax_1
WHERE
RowID = #RowCounter_1
SET #bill = (SELECT
CASE WHEN SUM(netvalue) IS NULL
THEN 0
ELSE SUM(netvalue)
END
FROM
tx_bill
WHERE
hlid = #hlid_
AND itemID NOT IN (8, 6)
AND YEAR(date_month) = YEAR(#tdate))
SET #sr = (SELECT
CASE WHEN SUM(asset * rate / 100) IS NULL
THEN 0
ELSE SUM(asset * rate / 100)
END
FROM
tx_bill
WHERE
hlid = #hlid_
AND itemID = 6
AND YEAR(date_month) = YEAR(#tdate))
INSERT INTO tx_bill_pay(clid, hlid, swercharge, pay_bill, pdate, bill_id, holdingNo, holding, ClientID, ClientName, billno, date_month, bill, install, inserted_by, inserted_date)
VALUES (#clid_, #hlid_, #sr, #bill / 4, DATEADD(day, -1, DATEADD(m, 3, #tdate)), CONCAT(#holdinNo_, YEAR(#tdate), '1'), #holdinNo_, #holding_, #clientid_, #clientName_, CONCAT(#holdinNo_, YEAR#tdate)), #tdate, #bill, 1, #userid, GETDATE())
INSERT INTO tx_bill_pay(clid, hlid, swercharge, pay_bill, pdate, bill_id, holdingNo, holding, ClientID, ClientName, billno, date_month, bill, install, inserted_by, inserted_date)
VALUES (#clid_, #hlid_, 0, 2 * (#bill / 4), DATEADD(day, -1, DATEADD(m, 6, #tdate)), CONCAT(#holdinNo_, YEAR(#tdate), '2'), #holdinNo_, #holding_, #clientid_, #clientName_, CONCAT(#holdinNo_, YEAR(#tdate)), #tdate, #bill, 2, #userid, GETDATE())
SET #RowCounter_1 = #RowCounter_1 + 1
END
DROP TABLE #tempProcess_tax_1
END
New query (1-2 mins)
ALTER PROCEDURE [dbo].[process_tax]
#userid varchar(10),
#remark nvarchar(500),
#tdate date ,
#roadno nvarchar(10),
#inst int
as
BEGIN
insert into tx_bill_pay(
clid,
hlid,
swercharge,
pay_bill,
pdate,
bill_id,
holdingNo,
holding,
ClientID,
ClientName,
billno,
date_month,
bill,
install ,
inserted_by,
inserted_date)
select
cl.clid,
cl.id,
swercharge=(select case when sum(asset*rate/100) is null then 0 else
sum(asset*rate/100) end from tx_bill where hlid=cl.id and
itemID =6 and year(date_month)=YEAR(#tdate)),
pay_bill=(select case when sum(netvalue) is null then 0 else
sum(netvalue) end from tx_bill where hlid=cl.id and itemID not
in(8,6) and year(date_month)=YEAR(#tdate))/4,
DATEADD(day,-1,
DATEADD(m,3,#tdate)),
CONCAT(cl.holdinNo, year(#tdate),'1'),
cl.holdinNo,
cl.holding,
cl.ClientID,
cl.clientName,
CONCAT(cl.holdinNo,
year(#tdate)),
#tdate,
bill=(select case when sum(netvalue) is null then 0 else sum(netvalue)
end from tx_bill where hlid=cl.id and itemID not in(8,6) and
year(date_month)=YEAR(#tdate))/4,
1,
#userid, getdate()
from
(select *
from tx_holding as cl
where cl.status=1 and cl.roadno=#roadno) AS cl
INNER JOIN (
select DISTINCT hlid from tx_asset where asset is not null
) AS A
ON Cl.id = A.hlid
INNER JOIN (
select DISTINCT id from tbl_client where client_type='Non-Govt.'
) AS C
ON cl.clid=C.id
WHERE NOT EXISTS
( SELECT 1
FROM tx_bill_pay as bp
WHERE year(date_month)=year(#tdate)
and bp.hlid=cl.id
)
insert into tx_bill_pay(clid,hlid
,swercharge,pay_bill,pdate,bill_id,holdingNo,holding,ClientID,
ClientName, billno, date_month, bill, install ,inserted_by,
inserted_date)
select
cl.clid,
cl.id,
0,
pay_bill=2*((select case when sum(netvalue) is null then 0 else sum(netvalue) end from tx_bill where hlid=cl.id and itemID not in(8,6) and year(date_month)=YEAR(#tdate))/4),
DATEADD(day,-1,
DATEADD(m,3,#tdate)),
CONCAT(cl.holdinNo, year(#tdate),'2'),
cl.holdinNo,
cl.holding,
cl.ClientID,
cl.clientName,
CONCAT(cl.holdinNo, year(#tdate)) ,
#tdate,
bill=(select case when sum(netvalue) is null then 0 else sum(netvalue)
end from tx_bill where hlid=cl.id and itemID not in(8,6) and year(date_month)=YEAR(#tdate))/4,
2,
#userid, getdate()
from
(select *
from tx_holding as cl
where cl.status=1 and cl.roadno=#roadno) AS cl
INNER JOIN (
select DISTINCT hlid from tx_asset where asset is not null
) AS A
ON Cl.id = A.hlid
INNER JOIN (
select DISTINCT id from tbl_client where client_type='Non-Govt.'
) AS C
ON cl.clid=C.id
WHERE cl.id not in
( SELECT hlid
FROM tx_bill_pay
WHERE year(date_month)=year(#tdate)
and hlid is not null group by hlid
)
Great Job removing the loop!
I will point out one possible performance problem, specifically year(date_month)=year(#tdate).
Because a column is wrapped in a function, it is non-SARGABLE. This means that date_month values cannot be evaluated directly and therefore, indexes and statistics for this column cannot be used.
To resolve this I suggest the following changes:
At the top of the SP define two more variables:
DECLARE #YearStart AS DATETIME, #NextYearStart DATETIME
SET #YearStart = DATEADD(yy, DATEDIFF(yy, 0, #tdate ), 0 )
SET #NextYearStart = DATEADD( yy, #YearStart, 1 )
Then replace all instances of year(date_month)=year(#tdate) with
#YearStart <= date_month AND date_month < #NextYearStart
This above expression looks for date_month values greater than or equal to midnight (notice that time component is accounted for) of the first day of the year and less than midnight of the next year.
Next, I would look at the query plan and see if SQL Server gives a "Missing Index" recommendation (it should appear just above the plan diagram, if it does suggest an index). Try adding the recommended missing indexes, then check to see if you get a performance improvement. If you don't get an improvement, remove the index - sometimes suggestions don't help.
Updated
Having 3 sub-queries (populating columns swercharge, pay_bill, bill) using tx_bill table with different WHERE conditions will cause at least 3 evaluations of this table per one main query execution.
It may, depending on the size of the table, be more efficient to calculate all 3 sub-queries once and save results to a temp table (or a table variable) as follows:
SELECT hlid,
ISNULL( SUM( CASE WHEN itemID NOT IN (8, 6) THEN netvalue END ), 0 ) AS Bill,
ISNULL( SUM( CASE WHEN itemID = 6 THEN asset * rate / 100 END ), 0 ) AS Sr
INTO #Bills
FROM tx_bill
WHERE #YearStart <= date_month AND date_month < #NextYearStart
AND NOT hlid IS NULL
GROUP BY hlid
In your main queries, join to this table as follows:
LEFT JOIN #SumBills AS SumBills ON SumBills.hlid = cl.hlid
and in SELECT change assignments as per below example:
pay_bill= ISNULL( SumBills.Bill, 0 ) / 4,
Note: I believe you have a bug in bill column, as the value is divided by 4, where it is not in the original cursor.
The last point:
As #GarethD has said in his answer to your previous question, properly formatting your code significantly reduces the time it takes to understand and change the code. I would go one step further and say that code format represents your attitude to and understanding of the problem at hand.
I added non clustered index then my query take 5 sec to run.That's almost solve my problem. #Alex Thanks for your hard work and time. I will check your tips.I Up vote your comment

SQL How to count all remains for each date

I have the following SQL function
CREATE FUNCTION [dbo].[GetCardDepartRemains]
(
#CardId INT,
#DepartId INT,
#Date DATETIME = NULL,
#DocumentId INT = NULL
)
RETURNS INT
AS
BEGIN
DECLARE #Res INT
SELECT
#Res = ISNULL(SUM(CASE WHEN Operations.[Output] = 0 AND Operations.RecipientId = #DepartId THEN 1 ELSE -1 END), 0)
FROM dbo.Operations
WHERE Operations.CardId = #CardId
AND (Operations.[Output] = 0 AND Operations.RecipientId = #DepartId OR Operations.Input = 0 AND Operations.SenderId = #DepartId)
AND (#Date IS NULL OR Operations.[Date] <= #Date)
RETURN #Res
END
GO
It counts remains for certain product on certain department on certain date.
If it is less then zero it means something's wrong with database
Now I need to find all remains for each card, for each department for all dates in database where result is wrong.
Theoretically speaking we can fing this by calling this procedure in a query like this
SELECT DISTINCT Operations.[Date] as [Date],
Departments.Id as Depart,
Cards.Id as [Card],
[dbo].[GetCardDepartRemains] (Cards.Id, Departments.Id,Operations.[Date],NULL) as Res
FROM [jewellery].[dbo].[Cards]
CROSS JOIN [jewellery].[dbo].[Departments]
CROSS JOIN [jewellery].[dbo].[Operations]
WHERE [dbo].[GetCardDepartRemains] (Cards.Id, Departments.Id,Operations.[Date],NULL) = -1
But this query executes more than 2 minutes, so we need to write a new query.
My query can find all remains for each card on each department on certain date (ex. '2016-10-04')
SELECT
[Card],
Depart,
Res
FROM
(SELECT
Cards.Id as [Card],
Departments.Id as Depart,
ISNULL(SUM(CASE WHEN Operations.[Output] = 0 AND Operations.RecipientId = Departments.Id THEN 1 ELSE -1 END), 0) as Res
FROM Operations
CROSS JOIN Cards
CROSS JOIN Departments
WHERE Operations.CardId = Cards.Id
AND (Operations.[Output] = 0 AND Operations.RecipientId = Departments.Id OR Operations.Input = 0 AND Operations.SenderId = Departments.Id)
AND (Operations.[Date] <= '2016-10-04')
GROUP BY Cards.Id, Departments.Id
) as X
WHERE Res = -1
Can you help to re-write this query to find remains for all dates?
Assuming SQL Server is 2008 or above:
To find all dates, just comment out the date filter like this:
-- AND (Operations.[Date] <= '2016-10-04')
If you need to filter on a date range:
AND (Operations.[Date] between between getDate()-30 and getDate()
Changing -30 to however many days in the past. So a year ago would be -364.

error in IF ELSE statement in SQL [duplicate]

This question already has an answer here:
Drop temp table within IF ELSE statement
(1 answer)
Closed 7 years ago.
I have the following stored procedure, But seems like the #Temp table is creating troubles in it. I get following error
There is already an object named '#Temp' in the database.
I guess somethings wrong with my IF ELSE
Here is the stored procedure:
create procedure spGetHOSalesReport
#DateFrom datetime,#DateTo datetime,#EmbossLine varchar(20),#CountryID int,#status char(2)
AS
Set #DateTo = #DateTo +1
if(#status = 'E1')
begin
Select PT.[ID] 'TransactionID', PT.BatchNumber, PT.SequenceNumber, PT.TransactionDate,
PT.TerminalID, PT.TotalAmount, PT.TransactionTypeID, TT.TransactionType,PT.PAN 'EmbossLine',PT.PreBalanceAmount, PT.PostBalanceAmount, RefTxnID, SettlementDate,PaidCash, CreditAmount, DiscountAmount,
RefPAN, PT.Remarks, ' ' + CashierCard as 'SupervisorCard',St.StoreID
into #Temp
from TempPOS_Transactions PT inner join TransactionType TT on TT.TransactionTypeID = PT.TransactionTypeID
inner join Staff St on St.CardNumber=PT.CashierCard
where
PT.[ID] not in (Select distinct isnull(TransactionID,0) from Testcards)
and (PT.TransactionDate >= #DateFrom) and (PT.TransactionDate < #DateTo)
and (PT.TransactionTypeID = 6) and (PT.BatchNumber = 0) and (Pt.SequenceNumber =-1)
select T.*, ' '+ C.EmbossLine+' ' as 'EmbossLine', C.EmbossLine as 'EmbossLine1',
isnull(C.FirstName,'') +' '+ isnull(C.LastName,'') 'EmbossName',C.FirstName,C.LastName,City.CityName,Country.CountryName,Country.CurrencyName, PM.MerchantID, PM.MerchantName1, C.AccountNumber, C.VehicleNumber, C.ExpiryDate ,
case C.Status when 'E0' then 'Authorized' when 'E1' then 'Pending' end 'Status'
from #Temp T
inner join Card C on C.EmbossLine= T.EmbossLine
inner join Terminal on Terminal.TerminalID = T.TerminalID
inner join Merchant PM on PM.MerchantID = Terminal.MerchantID
inner join City on City.CityID = PM.CityID
inner join Country on Country.CountryID = PM.CountryID
where C.Status <>'E3'
and C.CardID not in (Select distinct isnull(CardID,0) from Testcards)
and (C.EmbossLine like '%'+#EmbossLine+'%' or #EmbossLine like '-999')
and (PM.CountryID = #CountryID or #CountryID ='-999')
and (C.Status = #status)
order by T.TransactionDate, MerchantName1, T.BatchNumber, T.SequenceNumber
End
Else
Begin
Select PT.[ID] 'TransactionID', PT.BatchNumber, PT.SequenceNumber, PT.TransactionDate,
PT.TerminalID, PT.TotalAmount, PT.TransactionTypeID, TT.TransactionType,PT.PAN 'EmbossLine',PT.PreBalanceAmount, PT.PostBalanceAmount, RefTxnID, SettlementDate,PaidCash, CreditAmount, DiscountAmount,
RefPAN, PT.Remarks, ' ' + CashierCard as 'SupervisorCard',St.StoreID
into #Temp
from POS_Transactions PT inner join TransactionType TT on TT.TransactionTypeID = PT.TransactionTypeID
inner join Staff St on St.CardNumber=PT.CashierCard
where PT.[ID] not in (Select distinct isnull(TransactionID,0) from Testcards) and (PT.TransactionDate >= #DateFrom) and (PT.TransactionDate < #DateTo)
and (PT.TransactionTypeID = 6) and (PT.BatchNumber = 0) and (Pt.SequenceNumber =-1)
select T.*, ' '+ C.EmbossLine+' ' as 'EmbossLine', C.EmbossLine as 'EmbossLine1',
isnull(C.FirstName,'') +' '+ isnull(C.LastName,'') 'EmbossName',C.FirstName,C.LastName,City.CityName,Country.CountryName,Country.CurrencyName, PM.MerchantID, PM.MerchantName1, C.AccountNumber, C.VehicleNumber, C.ExpiryDate ,
case C.Status when 'E0' then 'Authorized' when 'E1' then 'Pending' end 'Status'
from #Temp T
inner join Card C on C.EmbossLine= T.EmbossLine
inner join Terminal on Terminal.TerminalID = T.TerminalID
inner join Merchant PM on PM.MerchantID = Terminal.MerchantID
inner join City on City.CityID = PM.CityID
inner join Country on Country.CountryID = PM.CountryID
where C.Status <>'E3'
and C.CardID not in (Select distinct isnull(CardID,0) from Testcards)
and (C.EmbossLine like '%'+#EmbossLine+'%' or #EmbossLine like '-999')
and (PM.CountryID = #CountryID or #CountryID ='-999')
and (C.Status = #status)
order by T.TransactionDate, MerchantName1, T.BatchNumber, T.SequenceNumber
End
drop table #Temp
You cannot have two statements in the same procedure that creates a temp table with the same name. This is a leftover from SQL 6.5 which did not have deferred name resolution.
And in any case, it only makes sense if the tables are created exactly the same, else your procedure will behave very funky.
So instead of using SELECT INTO, use CREATE TABLE + INSERT.
UPDATE
According to the selected way from comment:
Second option: First create temp table and insert
First let's create the temp table. For that you should modify your procedure like:
create procedure spGetHOSalesReport
#DateFrom datetime,#DateTo datetime,#EmbossLine varchar(20),#CountryID int,#status char(2)
AS
BEGIN -- begin procedure
SET #DateTo = #DateTo +1
if object_id('tempdb..#Temp') is not null drop table #Temp
create table #Temp
( TransactionID int
, BatchNumber ... ( your type of field )
, SequenceNumber ...
, TransactionDate ...
, TerminalID int
, TotalAmount ...
, TransactionTypeID int
, TransactionType ...
, EmbossLine ...
, PreBalanceAmount ...
, PostBalanceAmount ...
, RefTxnID int
, SettlementDate ...
, PaidCash ...
, CreditAmount ...
, DiscountAmount ...
, RefPAN ...
, Remarks ...
, SupervisorCard ...
, StoreID int
)
if(#status = 'E1')
.......
I do not know which data type has these fields, so, you have to do yourself. Then edit insert into temp table in first case and similar in another case:
insert into #Temp
Select PT.[ID] 'TransactionID', PT.BatchNumber, PT.SequenceNumber, PT.TransactionDate,
PT.TerminalID, PT.TotalAmount, PT.TransactionTypeID, TT.TransactionType,PT.PAN 'EmbossLine',PT.PreBalanceAmount, PT.PostBalanceAmount, RefTxnID, SettlementDate,PaidCash, CreditAmount, DiscountAmount,
RefPAN, PT.Remarks, ' ' + CashierCard as 'SupervisorCard',St.StoreID
from TempPOS_Transactions PT inner join TransactionType TT on TT.TransactionTypeID = PT.TransactionTypeID
inner join Staff St on St.CardNumber=PT.CashierCard
where ...
In the end of procedure you can add:
End -- end of your if
if object_id('tempdb..#Temp') is not null drop table #Temp
END -- end of procedure
But the simplest way is create two different temp tables:
if(#status = 'E1')
begin
if object_id('tempdb..#Temp1') is not null drop table #Temp1
Select PT.[ID] 'TransactionID', PT.BatchNumber, ...
into #Temp1
from TempPOS_Transactions PT
inner join TransactionType TT on TT.TransactionTypeID = PT.TransactionTypeID
.....
end
else
begin
if object_id('tempdb..#Temp2') is not null drop table #Temp2
Select PT.[ID] 'TransactionID', PT.BatchNumber, ...
into #Temp2
from POS_Transactions PT
inner join TransactionType TT on TT.TransactionTypeID = PT.TransactionTypeID
....
end
Also, you can write just two select without creating temp table in this case

SSRS - Error converting data type nvarchar to datetime

I have a known working report that I added a parameter and "if" statement to. Since adding, I now receive an error when previewing the report in SSRS:
Error converting data type nvarchar to datetime
The parameter I recently added is char, not datetime or nvarchar so I am not clear if this error is related to the newly added parameter. I can parse or run the query without an issue if I set the parameters in SSMS. There are two locations I have edited since the query's last known working state, they are notated in the query. Thoughts?
CREATE PROCEDURE [dbo].[LRP_ContributionDetail_BASE_trn]
(#fyear int= null,
#fyear_end int = null,
#start_dt_post datetime= null,
#end_dt_post datetime=null,
#list_no int = 0,
#start_dt datetime = null,
#end_dt datetime = null,
#include_restricted char (1) = null --recently added parameter
)
AS
SET ANSI_WARNINGS OFF
SET CONCAT_NULL_YIELDS_NULL OFF
SET NOCOUNT ON
if #fyear_end is null or #fyear_end = 0
set #fyear_end = #fyear
if #start_dt_post is null
set #start_dt_post = '1/1/1900'
if #start_dt is null
set #start_dt = '1/1/1900'
if #end_dt_post is null set #end_dt_post = GETDATE()
if #end_dt is null set #end_dt = GETDATE()
SELECT CONT.ref_no As Contribution_ID,
CONT.customer_no,
CUST.lname,
CUST.fname,
CAT.id As Cat_ID,
CAT.description AS Cat_Desc,
CAMP.fyear As FiscalYear,
CAMP.campaign_no As Camp_ID,
CAMP.description AS Camp_Desc,
FUND.fund_no As Fund_ID,
FUND.description AS Fund_Desc,
CDES.id As Desig_ID,
CDES.description As Desig_Desc,
CONT.cont_type,
CONT.cont_dt,
cont.cont_amt,
CONT.recd_amt,
(cont.cont_amt-CONT.recd_amt) AS Balance,
TRN_AMT = SUM(x.trn_amt), --show actual transactions
post_no = 0,--BAT.post_no,
MORG.memb_org_no as MembType_ID,
MORG.description as MembType_Desc,
fund.nonrestricted_income_gl_no
into #work
FROM T_TRANSACTION x
join T_CONTRIBUTION cont on x.ref_no = cont.ref_no
INNER JOIN T_CAMPAIGN CAMP ON x.campaign_no=CAMP.campaign_no
INNER JOIN TR_CAMPAIGN_CATEGORY CAT ON CAMP.category=CAT.id
INNER JOIN T_CUSTOMER CUST ON CONT.customer_no=CUST.customer_no
join T_BATCH bat on x.batch_no = bat.batch_no
join T_FUND fund on x.fund_no = fund.fund_no
LEFT OUTER JOIN TR_CONT_DESIGNATION CDES ON CONT.cont_designation=CDES.id
LEFT OUTER JOIN T_MEMB_ORG MORG ON CAMP.memb_org_no=MORG.memb_org_no
WHERE
x.trn_type in (1,2,5,4) --gift/pledge
and (#fyear is null or camp.fyear between #fyear and #fyear_end)
and cont.cont_dt between #start_dt and #end_dt --contribution date
and bat.posted_dt between isnull(#start_dt_post,'1/1/1900') and isnull(#end_dt_post,getdate()) and --posting date
(Coalesce(#list_no, 0) = 0
or Exists (Select * From [dbo].T_LIST_CONTENTS
Where customer_no = cont.customer_no
and list_no = #list_no))
and cont.cont_amt > 0
--and cont.ref_no = 549924
GROUP BY
cONT.ref_no,CONT.customer_no,CUST.lname,CUST.fname,CAT.id,CAT.description,
CAMP.fyear,CAMP.campaign_no,CAMP.description,FUND.fund_no,FUND.description,
CDES.id,CDES.description,CONT.cont_type,CONT.cont_dt,CONT.recd_amt,
MORG.memb_org_no,MORG.description, cont.cont_amt, fund.nonrestricted_income_gl_no
if #include_restricted = 'N' --recently added if statement
begin
--find where resticted activity takes place
select b.ref_no, b.trn_type, trn_amt = case when b.trn_type = 11 then SUM(b.trn_amt)* (-1)
else SUM(b.trn_amt) end
into #rest
from #work a
join t_transaction b on a.Contribution_ID = b.ref_no
where b.trn_type in (10,11)
group by b.ref_no, b.trn_type
select ref_no, rest_amt = SUM(trn_amt)
into #rest_sum
from #rest
group by ref_no
--delete where money is still restricted
delete #work
from #rest_sum a
where a.ref_no = #work.Contribution_ID
and a.rest_amt = #work.cont_amt
--only show unresricted amount
update #work
set cont_amt = cont_amt - a.rest_amt,
recd_amt = recd_amt - a.rest_amt
from #rest_sum a
where a.ref_no = #work.Contribution_ID
and a.rest_amt > 0
update #work
set Balance = cont_amt - recd_amt
end
select * from #work
EXEC LUP_Log_ProcedureCall #ObjectID = ##PROCID;
You should pass in all of the dates like this :
YYYYMMDD
take note, no hyphens or dashes in there
See this link for more
In the "dataset properties" box in Visual Studio I was executing the sproc and listing the parameters. I removed the new #include_restricted parameter from here, and set a default value in the sproc like this:
if #include_restricted is null set #include_restricted = 'N'
Also changed the tail of the sproc to include login for wether #include_restricted was a yes or no instead of just a no. Here is the revised working sproc:
USE [impresario]
GO
/****** Object: StoredProcedure [dbo].[LRP_ContributionDetail_BASE_trn] Script Date: 03/10/2015 10:31:50 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[LRP_ContributionDetail_BASE_trn](
#fyear int= null,
#fyear_end int = null,
#start_dt_post datetime= null,
#end_dt_post datetime=null,
#list_no int = 0,
#start_dt datetime = null,
#end_dt datetime = null
,#include_restricted char (1) = null
)
AS
SET ANSI_WARNINGS OFF
SET CONCAT_NULL_YIELDS_NULL OFF
SET NOCOUNT ON
if #fyear_end is null or #fyear_end = 0 set #fyear_end = #fyear
if #start_dt_post is null set #start_dt_post = '19000101'
if #start_dt is null set #start_dt = '19000101'
if #end_dt_post is null set #end_dt_post = GETDATE()
if #end_dt is null set #end_dt = GETDATE()
if #include_restricted is null set #include_restricted = 'N'
SELECT CONT.ref_no As Contribution_ID,
CONT.customer_no,
CUST.lname,
CUST.fname,
CAT.id As Cat_ID,
CAT.description AS Cat_Desc,
CAMP.fyear As FiscalYear,
CAMP.campaign_no As Camp_ID,
CAMP.description AS Camp_Desc,
FUND.fund_no As Fund_ID,
FUND.description AS Fund_Desc,
CDES.id As Desig_ID,
CDES.description As Desig_Desc,
CONT.cont_type,
CONT.cont_dt,
cont.cont_amt,
CONT.recd_amt,
(cont.cont_amt-CONT.recd_amt) AS Balance,
TRN_AMT = SUM(x.trn_amt), --show actual transactions
post_no = 0,--BAT.post_no,
MORG.memb_org_no as MembType_ID,
MORG.description as MembType_Desc,
fund.nonrestricted_income_gl_no
into #work
FROM T_TRANSACTION x
join T_CONTRIBUTION cont on x.ref_no = cont.ref_no
INNER JOIN T_CAMPAIGN CAMP ON x.campaign_no=CAMP.campaign_no
INNER JOIN TR_CAMPAIGN_CATEGORY CAT ON CAMP.category=CAT.id
INNER JOIN T_CUSTOMER CUST ON CONT.customer_no=CUST.customer_no
join T_BATCH bat on x.batch_no = bat.batch_no
join T_FUND fund on x.fund_no = fund.fund_no
LEFT OUTER JOIN TR_CONT_DESIGNATION CDES ON CONT.cont_designation=CDES.id
LEFT OUTER JOIN T_MEMB_ORG MORG ON CAMP.memb_org_no=MORG.memb_org_no
WHERE
x.trn_type in (1,2,5,4) --gift/pledge
and (#fyear is null or camp.fyear between #fyear and #fyear_end)
and cont.cont_dt between #start_dt and #end_dt --contribution date
and bat.posted_dt between isnull(#start_dt_post,'19000101') and isnull(#end_dt_post,getdate()) and --posting date
(Coalesce(#list_no, 0) = 0
or Exists (Select * From [dbo].T_LIST_CONTENTS
Where customer_no = cont.customer_no
and list_no = #list_no))
and cont.cont_amt > 0
--and cont.ref_no = 549924
GROUP BY
cONT.ref_no,CONT.customer_no,CUST.lname,CUST.fname,CAT.id,CAT.description,
CAMP.fyear,CAMP.campaign_no,CAMP.description,FUND.fund_no,FUND.description,
CDES.id,CDES.description,CONT.cont_type,CONT.cont_dt,CONT.recd_amt,
MORG.memb_org_no,MORG.description, cont.cont_amt, fund.nonrestricted_income_gl_no
if #include_restricted = 'Y'
select * from #work
if #include_restricted = 'N'
begin
--find where resticted activity takes place
select b.ref_no, b.trn_type, trn_amt = case when b.trn_type = 11 then SUM(b.trn_amt)* (-1)
else SUM(b.trn_amt) end
into #rest
from #work a
join t_transaction b on a.Contribution_ID = b.ref_no
where b.trn_type in (10,11)
group by b.ref_no, b.trn_type
select ref_no, rest_amt = SUM(trn_amt)
into #rest_sum
from #rest
group by ref_no
--delete where money is still restricted
delete #work
from #rest_sum a
where a.ref_no = #work.Contribution_ID
and a.rest_amt = #work.cont_amt
--only show unresricted amount
update #work
set cont_amt = cont_amt - a.rest_amt,
recd_amt = recd_amt - a.rest_amt
from #rest_sum a
where a.ref_no = #work.Contribution_ID
and a.rest_amt > 0
update #work
set Balance = cont_amt - recd_amt
select * from #work
end
EXEC LUP_Log_ProcedureCall #ObjectID = ##PROCID;
To convert nvarchar to date type and while extracting to excel the filter type also need to be data type filter then do the following steps:
In report design click on expression for the field text box.
Using CDate(Fields!postdate.vale) function converts to date type.
To get the required date format hi to text box properties and on the Number tab select custom and give the format style like MM-dd-yyyy.
**Postdate is the field name Result: 02-29-2021.