Improve INSERT INTO SELECT Query performance? - sql

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

Related

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 2 counts or 2 sums when a date column is selected

I've been trying to figure this out, but can't get it right.
I want to select the ID, table S date, 2 different counts for the number of Type = 1 and Type = 2 where the date in table P is >= the max date from table S where note = J, and a date difference between the max table P date and the table S date.
Example table data:
Table S
Date ID Note
2/26/2014 688606 J
2/14/2014 688606 J
Table P
Date ID Type
7/10/2015 688606 1
7/9/2015 688606 1
7/8/2015 688606 1
7/7/2015 688606 2
1/2/2010 688606 1
1/1/2010 688606 2
Ideal result set:
S.Date S.ID P.MaxDate P.T1Count P.T2Count P.DateDiff
2/26/2014 688606 7/10/2015 3 1 499
I've tried SUMs, but then I can't figure out how to incorporate the dates since SUM requires a group by and the dates are all unique.
I tried counts with a partition by the ID, but that makes the count include all data in table P rather than just those with dates >= table S.
Any help is really appreciated.
Thanks,
Here's my go at it with only 1 count:
SELECT
S.ID,
S.DATE as SDATE,
P.DATE as PDATE,
P.TYPE,
COUNT(P.DATE) OVER (PARTITION BY P.ID) as NBR_T1,
(P.DATE - S.DATE)as NBR_DATES
FROM
P
JOIN
(
SELECT
DISTINCT(S.ID),
S.DATE,
S.NOTE
FROM
S
WHERE
S.DATE = (SELECT MAX(SS.DATE)
FROM SS
WHERE SS.ID = S.ID
AND SS.NOTE = 'J')
AND S.ID = '688606'
) S ON P.ID = S.ID
WHERE
P.TYPE = '1'
AND P.ID = '688606'
AND P.DATE >= S.DATE
ORDER BY
P.DATE DESC
This is probably easiest if you aggregate the data in S first—I like CTEs for this—and then apply the necessary aggregation to the corresponding data in P. Like so:
-- Sample data.
declare #s table ([Date] date, [ID] bigint, [Note] char(1));
insert #s values
('2014-02-26', 688606, 'J'),
('2014-02-14', 688606, 'J');
declare #p table ([Date] date, [ID] bigint, [Type] int);
insert #p values
('2015-07-10', 688606, 1),
('2015-07-09', 688606, 1),
('2015-07-08', 688606, 1),
('2015-07-07', 688606, 2),
('2010-01-02', 688606, 1),
('2010-01-01', 688606, 2);
-- Step 1: Aggregate and filter the records you want in #s.
with [GroupS] as
(
select
[S].[ID],
[Date] = max([S].[Date])
from
#s [S]
where
[S].[Note] = 'J'
group by
[S].[ID]
)
-- Step 2: Aggregate the corresponding records in #p.
select
[S.Date] = [GroupS].[Date],
[S.ID] = [GroupS].[ID],
[P.MaxDate] = max([P].[Date]),
[P.T1Count] = sum(case [P].[Type] when 1 then 1 else 0 end),
[P.T2Count] = sum(case [P].[Type] when 2 then 1 else 0 end),
[P.DateDiff] = datediff(day, [GroupS].[Date], max([P].[Date]))
from
[GroupS]
inner join #p [P] on [GroupS].[ID] = [P].[ID]
where
[GroupS].[Date] <= [P].[Date]
group by
[GroupS].[Date],
[GroupS].[ID];
If you really only have 2 types then this solution will work. If you have N types and you therefor want N columns that you'd need to use a PivotTable.
Hopefully this helps.
USE SomeDB;
CREATE TABLE S (dt DATETIME NOT NULL, sTableid INT NOT NULL);
INSERT INTO S VALUES ('2/26/2014', 688606), ('2/14/2014', 688606);
CREATE TABLE P (dt DATETIME NOT NULL, pTableid INT NOT NULL, typeID INT NOT NULL);
INSERT INTO P VALUES
('7/10/2015', 688606, 1),
('7/9/2015', 688606, 1),
('7/8/2015', 688606, 1),
('7/7/2015', 688606, 2),
('1/2/2010', 688606, 1),
('1/1/2010', 688606, 2);
WITH tmp AS (
SELECT pTableid, p.typeID, MAX(dt) maxDate, COUNT(1) cnt
FROM dbo.P
GROUP BY pTableid, p.typeID
)
SELECT s.dt,
s.sTableid,
(SELECT MAX(maxDate) FROM tmp WHERE tmp.pTableid = s.sTableid) maxDateOfAnyType,
(SELECT cnt FROM tmp WHERE tmp.pTableid = s.sTableid AND tmp.typeID = 1) t1Count,
(SELECT cnt FROM tmp WHERE tmp.pTableid = s.sTableid AND tmp.typeID = 2) t2Count,
DATEDIFF(DAY, s.dt, (SELECT MAX(maxDate) FROM tmp WHERE tmp.pTableid = s.sTableid)) daysBetween
FROM dbo.S s
DROP TABLE S;
DROP TABLE P;
Another way of doing it... I assume you have table S and P already created and using SQL Server 2012+.
Declare #MDate datetime
SELECT #MDate = MAX(SS.DATE) FROM S SS WHERE SS.NOTE = 'J'
Select S.Date, S.ID, Max(P.Date) as 'MaxDate',
COUNT(IIF(convert(int,P.Type)=1, 1, NULL)) AS T1Count,
COUNT(IIF(convert(int,P.Type)=2, 1, NULL)) AS T2Count,
datediff(day, S.Date, Max(P.Date)) as 'DateDiff' from P
inner join S on P.ID = S.ID and S.Date >= #MDate
and P.Date >= #MDate
group by S.Date, S.ID

suddenly query executing slowly

I have very typical situation where I'm doing wrong. I have query which have executed fast initially but later on it taking loads of time to execute ..
My query :
Declare
#fromdate varchar(20) = '01/01/2014',
#todate varchar(20)= '27/05/2015',
#SERVICE_ID CHAR(5) = '123'
DECLARE #FDATE DATETIME ,
#TDATE DATETIME
SET #FDATE = (CONVERT(DATETIME,#fromdate,103))
SET #TDATE = (CONVERT(DATETIME,#todate,103))
IF OBJECT_ID('tempdb..#RUID') IS NOT NULL
DROP TABLE #RUID
CREATE TABLE #RUID(
OFFICEID INT,
OFFICE_TITTLE INT,
MAIN_OFFICE_TITTLE VARCHAR(50),
RLB_NAME VARCHAR(20),
DIST_NAME INT,
district_description VARCHAR(30))
CREATE CLUSTERED INDEX IDX_C_RUID_ID ON #RUID(OFFICEID)
CREATE NONCLUSTERED INDEX IDX_RUID_Name ON #RUID(OFFICE_TITTLE,DIST_NAME)INCLUDE(district_description)
INSERT INTO #RUID
SELECT OFFICEID,
OFFICE_TITTLE,
MAIN_OFFICE_TITTLE,
RLB_NAME,
DIST_NAME,
D.district_description
FROM APSDC..DISTRICT D
INNER JOIN cdma..Unified_RUID_WARD_MSTR I WITH(NOLOCK)
ON D.CDMA_DistrictID = I.DIST_NAME
WHERE RLB_NAME in(3) AND I.STATEID ='01'
select C.MAIN_OFFICE_TITTLE AS 'OFFICE_TITTLE',C.officeid, C.DIST_NAME AS DistrictName, C.district_description,
ISNULL(count(I.ApplicationNumber),0) 'Total_Trans',
isnull(sum(case when Data_Available='Y' AND DataTampered = 'N' then 1 else 0 end),0) 'CategoryA'
from #RUID c with(nolock)
LEFT JOIN Unified_BirthDeathAppDetails I WITH(NOLOCK) ON
(C.OFFICE_TITTLE=I.RUID AND C.DIST_NAME=I.DistrictName)
AND I.Service_Type= '01' AND
(DATEADD(DD,0,DATEDIFF(DD,0,I.Created_Date))) BETWEEN #FDATE AND #TDATE
AND NOT EXISTS(select application_number from reversal_details WITH(NOLOCK) WHERE ApplicationNumber <> i.ApplicationNumber AND service_id='123' )
group by C.MAIN_OFFICE_TITTLE,C.officeid, C.DIST_NAME,C.district_description
order by C.district_description ,C.MAIN_OFFICE_TITTLE
I have tried with #temp table and table variable but it is not even showing any result set. But the same query executed in 2 secs now it is taking lot of time. I have tried UPDATE Statstics on this tables and I have checked with locking also. What I need to do I have followed every other peformance optimized techinique.
Try:
Declare
#FDate Date = '01/01/2014',
#TDate Date= '27/05/2015',
#SERVICE_ID CHAR(5) = '123'
;WITH RUID
AS
(
SELECT OFFICEID,
OFFICE_TITTLE,
MAIN_OFFICE_TITTLE,
RLB_NAME,
DIST_NAME,
D.district_description
FROM APSDC..DISTRICT D
INNER JOIN cdma..Unified_RUID_WARD_MSTR I
ON D.CDMA_DistrictID = I.DIST_NAME
WHERE RLB_NAME in(3) AND I.STATEID ='01'
),
AppDetails
AS
(
SELECT ApplicationNumber,
CASE WHEN Data_Available='Y' AND DataTampered = 'N'
THEN 1
ELSE 0
END CategoryA
FROM Unified_BirthDeathAppDetails I
WHERE I.CreateDate >= #FDate AND I.CreatedDate < #TDate AND
NOT EXISTS
( select application_number
FROM reversal_details
WHERE I.Service_Type= '01' AND
ApplicationNumber <> i.ApplicationNumber AND
service_id= #Service_iD
)
)
SELECT C.MAIN_OFFICE_TITTLE AS OFFICE_TITTLE
,C.officeid, C.DIST_NAME AS DistrictName
, C.district_description
,ISNULL(count(I.ApplicationNumber),0) Total_Trans
,isnull(sum(CategoryA),0) CategoryA
FROM RUID c
LEFT JOIN AppDetails I
ON C.OFFICE_TITTLE=I.RUID AND C.DIST_NAME=I.DistrictName
GROUP BY C.MAIN_OFFICE_TITTLE
,C.officeid
,C.DIST_NAME
,C.district_description
ORDER BU C.district_description
,C.MAIN_OFFICE_TITTLE
Make sure you have decent indexes on RLB_name, StateId, CDMA_DistrictId, Dist_Name, CreatedDate etc.

Show 0 in count SQL

This is my result :
Year matches
2005 1
2008 2
and this is my expected result:
Year matches
2005 1
2006 0
2007 0
2008 2
This is what I have tried:
SELECT DATEPART(yy,A.match_date) AS [Year], COUNT(A.match_id) AS "matches"
FROM match_record A
INNER JOIN match_record B ON A.match_id = B.match_id
WHERE (score) IS NULL OR (score) = 0
GROUP BY DATEPART(yy,A.match_date);
I want to get zero as count in the years where score have some values(not null and zero, anything greater than 0) . Can someone help me?
This might do what you're looking for:
SELECT DATEPART(yy,A.match_date) AS [Year],
SUM(CASE WHEN score=0 or score is null THEN 1 ELSE 0 END) AS "matches"
FROM match_record A
INNER JOIN match_record B ON A.match_id = B.match_id
GROUP BY DATEPART(yy,A.match_date);
Assuming you have any data in the missing years, this should now produce your expected results.
If, instead, you need 0s for years where you have no data, you'll need to provide the list of years separately (say, via a numbers table) and then LEFT JOIN that source to your existing query.
Consider following is your table
SELECT * INTO #TEMP FROM
(
SELECT 2005 [YEARS],1 [MATCHES]
UNION ALL
SELECT 2008,2
)T
Declare two variables to get min and max date in your table
DECLARE #MINYEAR int;
DECLARE #MAXYEAR int;
SELECT #MINYEAR = MIN(YEARS) FROM #TEMP
SELECT #MAXYEAR = MAX(YEARS) FROM #TEMP
Do the following recursion to get years between the period in your table and LEFT JOIN with your table.
; WITH CTE as
(
select #MINYEAR as yr FROM #TEMP
UNION ALL
SELECT YR + 1
FROM CTE
WHERE yr < #MAXYEAR
)
SELECT DISTINCT C.YR,CASE WHEN T.MATCHES IS NULL THEN 0 ELSE T.MATCHES END MATCHES
FROM CTE C
LEFT JOIN #TEMP T ON C.yr=T.YEARS
DECLARE #t table(Year int, matches int)
DECLARE #i int=2005
WHILE #i <=2008
BEGIN
IF NOT exists (SELECT matches FROM tbl WHERE year=#i)
BEGIN
INSERT INTO #t
SELECT #i,'0'
SET #i=#i+1
END
else
BEGIN
INSERT INTO #t
SELECT year,[matches] from tbl
SET #i=#i+1
END
END
SELECT DISTINCT * FROM #t
how about,
SELECT
[year],
COUNT(*) [matches]
FROM (
SELECT
DATEPART(yy, [A].[match_date]) [year]
FROM
[match_record] [A]
LEFT JOIN
[match_record] [B]
ON [A].[match_id] = [B].[match_id]
WHERE
COALESCE([B].[score], 0) = 0) [Nils]
GROUP BY
[Year];

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