Query takes too long because of <> in where clause - sql

I'm building a script to send by email. I've a table with transactions and I want to send by email only when status is "failed" . The table has now ~ 50 millions of rows and it will keep increasing and I cannot archive it.
this is the query:
if exists (select top 1 1 from TicketTransactionDetails where ServiceStatus != 0)
begin
DECLARE #tableHTML NVARCHAR(MAX) ;
SET #tableHTML =
N'<H1>Transactions Status</H1>' +
N'<table border="1">' +
N'<tr><th>TransactionDate</th><th>NbOfRecords</th>' +
N'<th>ServiceStatus</th><th>StatusDescription</th><th>RunDate</th></tr>' +
CAST ( (select td = CAST(InsertDate as date), '',
td = COUNT(*),'',
td = ServiceStatus,'',
td = b.StatusDescription,'',
td = GETDATE()
from TicketTransactionDetails a
inner join ServiceStatuses b on a.ServiceStatus = b.StatusID
where ServiceStatus = 0
group by CAST(InsertDate as date),ServiceStatus,b.StatusDescription
order by CAST(InsertDate as date) desc
FOR XML PATH('tr'), TYPE
) AS NVARCHAR(MAX) ) +
N'</table>' ;
exec msdb.dbo.sp_send_dbmail
#profile_name = 'DB_Maintenance',
#recipients = 'a.a#a.a',
#subject = 'Tranzanctions with errors',
#body = #tableHTML,
#body_format = 'html';
end
What takes a lot to execute (~ 25sec) is:
select top 1 1 from TicketTransactionDetails where ServiceStatus != 0
Is there way to improve my query?
Many thanks in advance.

A couple of things you can do -
Firstly, check whether you have an index on the column ServiceStatus. If not then create it. That really ought to do it.
The other option, although risky is to add a (nolock) hint to your query. This will bypass any locks held on the table
select top 1 1 from TicketTransactionDetails (nolock) where ServiceStatus != 0

Use count instead, count perform much better than top
if exists (select count(1) from TicketTransactionDetails where ServiceStatus != 0)

Related

Stored procedure sometimes takes a long time to execute query in SQL Server

I have strange issue and I am getting more confused about this. I am using UWP on the .NET framework and using SQL Server as database. The problem is my application working too much slow and when I check my stored procedure execution time then I am notice that may be a problem is from the database stored procedure side.
My stored procedure sometimes takes 4-5 seconds to execute and sometimes takes more like 15-16 seconds and sometimes it takes more than 1 minute.
My questions are:
Why is it taking a lot more time sometimes, but not every time?
How can I solve this issue and boost my application execution time?
This is the stored procedure:
ALTER PROCEDURE [dbo].[SPNAME]
#HouseID INT,
#FromDate DATETIME,
#ToDate DATETIME,
#PageNumber INT,
#PageSize INT,
#RollID INT
AS
BEGIN
DECLARE #RoomID INT = 0
DECLARE #UnitID INT = 0
DECLARE #HouseCode INT = 0
DECLARE #StartRow INT
DECLARE #EndRow INT
SET #StartRow = #PageNumber
SET #StartRow = ((#StartRow-1) * #PageSize) + 1
SET #EndRow = (#StartRow + #PageSize) - 1;
IF OBJECT_ID('tempdb.dbo.#TableName') IS NOT NULL
DROP TABLE dbo.#TableName
SELECT
#HouseCode = [HouseCode],
#UnitID = UnitId
FROM
[dbo].[tblHouse]
WHERE
[HouseId] = #HouseID
SELECT #RoomID = RoomCode
FROM [dbo].[tblUnit] u
LEFT JOIN tblRoom h ON u.RoomID = h.Room
WHERE UnitID = #UnitID
SELECT MAX(RoNum) RoNum
INTO dbo.#tableName
FROM [dbo].[TAbleNAme]
WHERE FanId = #HouseCode
AND InActive = 0
AND ((InactivatedOn IS NULL AND StopDate IS NULL)
OR (ISNULL(InactivatedOn, StopDate) BETWEEN #FromDate AND #ToDate)
OR (ISNULL(InactivatedOn,StopDate) >= #ToDate))
AND (FirstFillDate <= #ToDate)
GROUP BY
OrigRoNum
SELECT
K.Topic, K.Comment, K.RoNum
INTO
#TmptblRoCom
FROM
[dbo].[TableNAme] K
LEFT JOIN
#TableName T ON K.RoNum = T.RoNum
WHERE
K.Topic = 'Nar Auto'
AND ISNUMERIC(SUBSTRING(K.Comment, 0, 3)) = 1
SELECT ButtonTypeID, ISNULL(SortOrder, 17) AS SortOrder
INTO dbo.#TableName
FROM [dbo].[TAbleName]
WHERE NHID = #RoomID AND NHSortID = 29
SELECT
DIN,
'LU: ' + LTRIM(RTRIM(ReasonCode)) +
'Exp:' + ISNULL(FORMAT(ExpiryDate, 'dd/MM/yyyy'), 'Indefinite') AS HLCode
INTO
dbo.#TableName
FROM
[dbo].[TableNAme]
WHERE
PatID = #HouseCode
AND (ExpiryDate IS NULL OR ExpiryDate > GETDATE())
SELECT
X.DgID AS DumbID,
X.ID AS RID,
CASE
WHEN DR.BandName IS NULL
THEN '#' + dm.Description
ELSE CONCAT(BandName, ' ', strength)
END AS DugName,
StrengthType, FormType,
CASE
WHEN GericName IS NULL
THEN
CASE
WHEN BandName IS NULL
THEN '#' + dm.Description
ELSE CONCAT(DR.BandName, ' ', DR.strength)
END
ELSE CONCAT(DR.GericName, ' ', DR.strengthTypr)
END AS GericName,
DR.[Description],
dbo.GetDirectionWithFullForm (X.IG,' ') AS Direction,
CAST(X.RoNum AS BIGINT) AS RoNum,
FORMAT(X.FirstFillDate,'dd/MM/yyyy') AS FirstFillDate,
FORMAT(X.LastFillDate,'dd/MM/yyyy') AS DugOrderDate,
ISNULL(TP.[ID],0) AS RollID,
TP.[Cont] AS Cont,
TP.[DisContinue] AS Disc,
TP.[Hold] AS Hold,
CAST(m.OrderType AS INT) AS OrderType,
pl.HLCode,
rc.Comment,
TP.[Comments]
FROM
dbo.#TAbleNAme rn
INNER JOIN
[dbo].[RollRx] X ON rn.RoNum = X.RoNum
INNER JOIN
[dbo].[RollDg] DR ON X.[DgID] = DR.ID
LEFT JOIN
[dbo].[RollDgMix] DM ON X.MixID = DM.ID
LEFT JOIN
[dbo].[RollPreviousNumber] TP ON X.DrgID = TP.[RollMedicationID]
AND X.[ID] = TP.[RoID]
AND TP.[HouseID] = #HouseID
AND TP.[TableName] = #RollID
LEFT JOIN
dbo.#TableNAme m ON m.TypeID = X.TypeID
LEFT JOIN
dbo.#TableName pl ON pl.DNNUM = X.DNNUM
LEFT JOIN
dbo.#TableName rc ON rc.RoNum = X.RoNum
END
Execution Plan
Yes sounds like parameter sniffing or lack of memory issue (this has made it block for me at times when I am running to much on my box), look at the execution plan.
Run the stored proc with your parameters and see if there is a difference here, by right clicking on the execution plan select, below shows an example.

SQL Server 2008 R2 : Conversion failed when converting date and/or time from character string

I'm getting the following error
Conversion failed when converting date and/or time from character string
I can't get it to run properly please help me, here is my code (don't shame my loop i just need it to work).
The thing is i have a closed list with contracts IDs and a table with different dates and dates called #PERIODO it contains datetime type.
WHILE (SELECT TOP 1 FECHA1 FROM #PERIODO) > 0
BEGIN
SET #MES_ANTERIOR = (SELECT TOP 1 FECHA1 FROM #PERIODO ORDER BY FECHA1 ASC)
SET #MES_EN_CURSO = (SELECT TOP 1 B.FECHA1 FROM (SELECT TOP 2 A.FECHA1 FROM #PERIODO A ORDER BY FECHA1 ASC ) B ORDER BY B.FECHA1 DESC)
SET #MES_3 = (SELECT TOP 1 FECHA3 FROM #PERIODO ORDER BY FECHA1 ASC)
SET #MES_4 = (SELECT TOP 1 B.FECHA3 FROM (SELECT TOP 2 A.FECHA3 FROM #PERIODO A ORDER BY FECHA3 ASC ) B ORDER BY B.FECHA3 DESC)
SET #MES_ANT = LEFT(CONVERT(VARCHAR(8),#MES_ANTERIOR,112),8) -- SELECT #MES_ANT
SET #MES_CURR = LEFT(CONVERT(VARCHAR(8),#MES_EN_CURSO,112),8) -- SELECT #MES_CURR
EXEC('IF OBJECT_ID(''WORK.DBO.OPERACIONES_ABIF_'+#MES_CURR+''', ''U'') IS NOT NULL
DROP TABLE WORK.DBO.OPERACIONES_ABIF_'+#MES_CURR+' ')
SET #SQL1 = 'SELECT DISTINCT
A.*
INTO WORK.DBO.OPERACIONES_ABIF_'+#MES_CURR+'
FROM BDGESTION.DBO.BASE_RIESGOS_PROD_ALTAIR_'+#MES_CURR+' A WHERE '
SET #SQL2 = ' A.NUM_OPERACION IN (
SELECT
B.NUM_CONTRATO
FROM (
SELECT NUM_CONTRATO
FROM
( SELECT * FROM #OPER WHERE FECHA_INICIO IS NOT NULL) H
WHERE H.FECHA_INICIO < CAST(CONVERT(VARCHAR(12),'+#MES_EN_CURSO+',23) AS VARCHAR )
AND H.FECHA_INICIO >= CAST(CONVERT(VARCHAR(12),'+#MES_ANTERIOR+',23) AS VARCHAR )
) B
WHERE
B.NUM_CONTRATO IS NOT NULL ) '
EXEC (#SQL1 + #SQL2)
DELETE FROM #PERIODO
WHERE FECHA1 = #MES_ANTERIOR
END
The problem would appear to be these lines:
WHERE H.FECHA_INICIO < CAST(CONVERT(VARCHAR(12), ' + #MES_EN_CURSO + ', 23) AS VARCHAR)
Presumably, you want the cast() outside the aggregation:
WHERE H.FECHA_INICIO < ' + CONVERT(VARCHAR(12), #MES_EN_CURSO + ', 23) + '
However, I don't recommend this as a solution. The correct solution is to pass the value in as a parameter. Your query is too complex (and messy) for me to attempt that. You should look at the documentation for sp_executesql on how to properly construct dynamic SQL with parameters.
I think you should print #SQL2 as you go through the loop so you can see the incorrect value.
It would be helpful to show how you declare #MES_EN_CURSO and declare #MES_ANTERIOR.

how to get results of function with datatype nvarchar

I have a database table like this
Id Code Amount Formula
-------------------------------------
1 A01 20.00
2 A08 0.00 dbo.ufn_Test(40)
3 A03 0.00 dbo.ufn_Test(60)
My Formula column is a string with name as a function in my database, how can I return the result into the Amount column?
My table has about 100000 rows so when I used while() it takes a lot of time.
I'm using SQL Server 2012
I've used dynamic SQL like this:
DECLARE #_j INT = 1
WHILE (#_j<=(SELECT MAX(Id) FROM #Ct_Lv))
BEGIN
SET #_CtLv = (SELECT Formula FROM #Ct_Lv WHERE Id = #_j)
DECLARE #sql NVARCHAR(MAX)
DECLARE #result NUMERIC(18, 2) = 0
SET #sql = N'set #result = N''''SELECT''' + #_CtLv
EXEC sp_executesql #sql, N'#result float output', #result out
UPDATE #Ct_Lv
SET Amount = #result
WHERE Id = #_j
SET #_j = #_j + 1
END
but my max #_j = 100000, I've run my code for 3 hours and it's still running
one thing, i would like know here is, does id attribute is identity column ?
2nd most important part is, you are declaring variables #sql and #result for each row and you are taking max at every row iterate, which might decrease the performance. I am not sure, how much faster the solution i have been given here, but you can try it once.
Set Nocount On;
Declare #_count Int
,#_j Int
,#_cnt Int
,#_dynamicSql Varchar(Max)
,#_formula Varchar(Max)
,#_row25Cnt Int
Select #_count = Count(1)
,#_j = 0
,#_cnt = 0
,#_dynamicSql = ''
,#_formula = ''
,#_row25Cnt = 1
From #Ct_Lv As ct With (Nolock)
While (#_cnt < #_count)
Begin
Select Top 1
#_j = ct.Id
,#_formula = ct.Formula
From #Ct_Lv As ct With (Nolock)
Where ct.Id > #_j
Order By ct.Id Asc
Select #_dynamicSql = 'Update ct Set ct.Amount = f.result From #Ct_Lv As ct Join ( Select ' + Cast(#_j As Varchar(20)) + ' As Id, [fuctionResultAttribute] As result From ' + #_formula + ' ) As f On ct.Id = f.Id; '
If (#_row25Cnt = 25)
Begin
Exec (#_dynamicSql)
Select #_dynamicSql = ''
,#_row25Cnt = 0
End
Else If ((#_cnt + 1) = #_count)
Begin
Exec (#_dynamicSql)
Select #_dynamicSql = ''
,#_row25Cnt = 0
End
Select #_cnt = #_cnt + 1
,#_row25Cnt = #_row25Cnt + 1
End
Here, what i have done so far is, I am looping Id by Id and generating dynamic sql for each 25 rows, once count is reach to 25, that dynamic sql will be executed which will update your amount. and again start generating dynamic sql for next 25 rows, and when count is about to end and there would be no 25 rows as end then dynamic sql will be executed when loop about to end in 'else if' condition.
above my solution will work only in that case when there would be only one formula in formula column for each row.
I just suggest one thing if Formula field calling the same function each time then better to store only parameter that you want to pass to the function, then you can easily process over huge data.
Else looping over huge data is not preferable way to perform any operation. So it's advisable to use some other trick over there in table structure and storing data.

SQL Server Express stored procedure condition checking

I have written a stored procedure:
ALTER PROCEDURE [dbo].[GetBRs_Pager]
#PageIndex INT
,#PageSize INT
,#SupId INT
,#RecordCount INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT ROW_NUMBER()
OVER (
ORDER BY [tblBR].[ID] ASC
) AS RowNumber
,[tblBR].[ID]
,[tblBR].[BRName]
,[tblBR].[SupervisorId]
,[tblSupervisor].[SupervisorName]
,[tblBR].[BRCode]
,[tblBR].[BRMobile]
,[tblBR].[BRTypeId]
,[tblType].[TypeName]
,[tblBR].[BRImageUrl]
INTO #Results
FROM [tblBR]
INNER JOIN [tblType]
ON [tblBR].[BRTypeId] = [tblType].[ID]
INNER JOIN [tblSupervisor]
ON [tblBR].[SupervisorId] = [tblSupervisor].[ID]
where [tblBR].[Active] = 1
and [tblBR].[SupervisorId]=#SupId
SELECT #RecordCount = COUNT(*)
FROM #Results
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
DROP TABLE #Results
END
Now I want to modify the query as follows
if(#supId != 0) then where [tblBR].[Active] = 1
and [tblBR].[SupervisorId] = #SupId else [tblBR].[Active] = 1
How to do it? Anyone helps me is greatly appreciated. Thanks in advance.
Just basic logic, I think:
where [tblBR].[Active] = 1
and (
[tblBR].[SupervisorId]=#SupId or
#SupId = 0
)
You don't need control-flow statements or the like here. You just need to express the alternatives and link them together using boolean operators.
What I would do is the following thing, adding the following code after your where clauses
AND [tblBR].[Active] = 1
AND [tblBR].[SupervisorId] = CASE WHEN #supId != 0 THEN #supId ELSE [tblBR].[SupervisorId] END
the second and check if the variable if different than 0, in that case it filter the supervisorid with your variable, if equal 0 it filter the supervisorid field by his row value.

Update multiple columns by loop?

I have a select statement which I want to convert into an update statement for all the columns in the table which have the name Variable[N].
For example, I want to do these things:
I want to be able to convert the SQL below into an update statement.
I have n columns with the name variable[N]. The example below only updates column variable63, but I want to dynamically run the update on all columns with names variable1 through variableN without knowing how many variable[N] columns I have in advance. Also, in the example below I get the updated result into NewCol. I actually want to update the respective variable column with the results if possible, variable63 in my example below.
I want to have a wrapper that loops over column variable1 through variableN and perform the same respective update operation on all those columns:
SELECT
projectid
,documentid
,revisionno
,configurationid
,variable63
,ISNULL(Variable63,
(SELECT TOP 1
variable63
FROM table1
WHERE
documentid = t.documentid
and projectid=t.projectid
and configurationid=t.configurationid
and cast(revisionno as int) < cast(t.revisionno as int)
AND Variable63 is NOT NULL
ORDER BY
projectid desc
,documentid desc
,revisionno desc
,configurationid desc
)) as NewCol
FROM table1 t;
There's no general way to loop through variables in SQL, you're supposed to know exactly what you want to modify. In some databases, it will be possible to query system tables to dynamically build an update statement (I know how to do that in InterBase and it's decessor Firebird), but you haven't told us anything which database engine you're using.
Below is a way you could update several fields that are null, COALESCE and CASE are two way of doing the same thing, as is using LEFT JOIN or NOT EXISTS. Use the ones you and your database engine is most comfortable with. Beware that all records will be updated, so this is not a good solution if your database contains millions of records, each record is large and you want this query to be executed lots of times.
UPDATE table1 t
SET t.VARIABLE63 =
COALESCE(t.VARIABLE63,
(SELECT VARIABLE63
FROM table1 t0
LEFT JOIN table1 tNot
ON tNot.documentid = t.documentid
AND tNot.projectid=t.projectid
AND tNot.configurationid=t.configurationid
AND cast(tNot.revisionno as int) > cast(t0.revisionno as int)
AND cast(tNot.revisionno as int) < cast(t.revisionno as int)
AND tNot.Variable63 is NOT NULL
WHERE t0.documentid = t.documentid
AND t0.projectid=t.projectid
AND t0.configurationid=t.configurationid
AND cast(t0.revisionno as int) < cast(t.revisionno as int)
AND t0.Variable63 is NOT NULL
AND tNot.Variable63 is NULL)),
t.VARIABLE64 = CASE WHEN t.VARIABLE64 IS NOT NULL then t.VARIABLE64
ELSE (SELECT VARIABLE64
FROM table1 t0
WHERE t0.documentid = t.documentid
AND t0.projectid=t.projectid
AND t0.configurationid=t.configurationid
AND cast(t0.revisionno as int) < cast(t.revisionno as int)
AND t0.Variable64 is NOT NULL
AND NOT EXISTS(SELECT 1
FROM table1 tNot
WHERE tNot.documentid = t.documentid
AND tNot.projectid=t.projectid
AND tNot.configurationid=t.configurationid
AND cast(tNot.revisionno as int) > cast(t0.revisionno as int)
AND cast(tNot.revisionno as int) < cast(t.revisionno as int)
AND tNot.Variable64 is NOT NULL)) END
OK I think I got it. Function that loops through columns and runs an update command per column.
DECLARE #sql NVARCHAR(1000),
#cn NVARCHAR(1000)--,
--#r NVARCHAR(1000),
--#start INT
DECLARE col_names CURSOR FOR
SELECT column_name
FROM information_schema.columns
WHERE table_name = 'PIVOT_TABLE'
ORDER BY ordinal_position
--SET #start = 0
DECLARE #op VARCHAR(max)
SET #op=''
OPEN col_names FETCH next FROM col_names INTO #cn
WHILE ##FETCH_STATUS = 0
BEGIN
--print #cn
IF UPPER(#cn)<> 'DOCUMENTID' and UPPER(#cn)<> 'CONFIGURATIONID' and UPPER(#cn)<> 'PROJECTID' and UPPER(#cn)<> 'REVISIONNO'
BEGIN
SET #sql = 'UPdate pt
set pt.' + #cn + ' = ((SELECT TOP 1 t.' + #cn + ' FROM pivot_table t WHERE t.documentid = pt.documentid and t.projectid=pt.projectid
and t.configurationid=pt.configurationid and cast(t.revisionno as int) < cast(pt.revisionno as int) AND t.' + #cn + ' is NOT NULL
ORDER BY revisionno desc)) from PIVOT_TABLE pt where pt.' + #cn + ' is NULL;'
EXEC Sp_executesql
#sql
--print #cn
END
FETCH next FROM col_names INTO #cn
END
CLOSE col_names
DEALLOCATE col_names;