SQL text wrapping - sql-server

Hello and good afternoon. I am facing an issue with the system i support. I am able to build "Macros" which can pull sql views to a document. The issue is that the columns for these views do not wrap on the document. If a certain row contains too much text, it will push the other columns out of line. To resolve this i am trying to build a function to use with my view that will sort of brute force the wrap by looping through each row and creating an additional row to hold text for certain columns where the text limit was reached. I have something that works, but it's terribly slow at times. Does anyone have any ideas on how i can optimize this?
(
)
RETURNS #medlist2 TABLE (uniq_id UNIQUEIDENTIFIER, enterprise_id CHAR(5), practice_id CHAR (4), person_id UNIQUEIDENTIFIER,
enc_id UNIQUEIDENTIFIER, medication_name VARCHAR (70), sig_desc VARCHAR (512), start_date VARCHAR(10), row_num INT)
AS
BEGIN
DECLARE #medlist TABLE (uniq_id UNIQUEIDENTIFIER, enterprise_id CHAR (5), practice_id CHAR (4), person_id UNIQUEIDENTIFIER,
enc_id UNIQUEIDENTIFIER, medication_name VARCHAR (70), sig_desc VARCHAR (512), start_date DATETIME, processed INT)
DECLARE #medicationName VARCHAR (70)
DECLARE #sigDesc VARCHAR (512)
DECLARE #startDate VARCHAR (10)
DECLARE #uniqID UNIQUEIDENTIFIER
DECLARE #enterpriseID CHAR (5)
DECLARE #practiceID CHAR (4)
DECLARE #personID UNIQUEIDENTIFIER
DECLARE #encID UNIQUEIDENTIFIER
DECLARE #RowNum INT
DECLARE #RowCount INT
INSERT INTO #medlist (uniq_id, enterprise_id, practice_id, person_id,
enc_id, medication_name, sig_desc, start_date, processed)
SELECT uniq_id, enterprise_id, practice_id, person_id,
enc_id, medication_name, sig_desc, start_date, 0
FROM med_table
WHERE person_id IN (select distinct person_id from active_users where create_timestamp > GETDATE()-.2)
AND date_stopped = ''
ORDER BY medication_name
SET #RowCount = (SELECT COUNT(*) FROM #medlist WHERE processed = 0)
SET #RowNum = 0
WHILE #RowCount > 0
BEGIN
SET #RowNum = #RowNum + 1
SELECT TOP(1) #uniqid = uniq_id, #enterpriseID = enterprise_id, #practiceID = practice_id,
#personID = person_id, #encID = enc_id, #medicationName = '- ' +medication_name, #sigDesc = sig_desc,
#startDate = CONVERT(VARCHAR(10), start_date, 101)
FROM #medlist
WHERE processed = 0
INSERT INTO #medlist2(uniq_id, enterprise_id, practice_id, person_id,
enc_id, start_date, row_num, medication_name, sig_desc)
SELECT #uniqID, #enterpriseID, #practiceID, #personID, #encID, #startDate, #RowNum,
(CASE WHEN DATALENGTH(#medicationName) > 28 THEN LEFT(#medicationNAME, 28) + '-' ELSE #medicationName END),
(CASE WHEN DATALENGTH(#sigDesc) > 41 THEN LEFT(#sigDesc, 41) + '-' ELSE #sigDesc END)
WHILE DATALENGTH(#sigDesc) > 42 OR DATALENGTH(#medicationName) > 29
BEGIN
SET #medicationName = substring(#medicationName, 29,DATALENGTH(#medicationName))
SET #sigDesc = substring(#sigDesc, 42,DATALENGTH(#sigDesc))
SET #RowNum = #RowNum + 1
INSERT INTO #medlist2 (uniq_id, enterprise_id, practice_id, person_id,
enc_id, medication_name, sig_desc, row_num)
SELECT #uniqID, #enterpriseID, #practiceID, #personID, #encID, LEFT(#medicationNAME, 28), LEFT(#sigDesc, 41), #RowNum
IF DATALENGTH(#sigDesc) < 42 OR DATALENGTH(#medicationName) > 29
BREAK
ELSE
CONTINUE
END
UPDATE #medlist
SET processed = 1
WHERE uniq_id = #uniqID
SET #RowCount = (SELECT COUNT(*) FROM #medlist WHERE processed = 0)
IF #RowCount = 0
BREAK
ELSE
CONTINUE
END
RETURN
END

Don't do this in the database. Do it in your application layer!
Something as trivial as wrapping text is extremely expensive when SQL server is doing it on a row-by-row bases, but should be very quick to do in whatever application is displaying your results.

Related

SQL search for occurrences of transaction types within multiple date ranges

I have the following tables:
CREATE TABLE Users
(
UserID INT,
UserName VARCHAR(100)
)
CREATE TABLE Trans
(
UserID INT,
TransID INT,
TransTypeID INT,
TransDate DATETIME,
TransAmount DECIMAL(15,2)
)
CREATE TABLE TransType
(
TransTypeID INT,
TransTypeName VARCHAR(10)
)
Lets say for simplicity there are only 2 entries in TransType table:
Income
Expense
I'm looking to find out if 3 or more transactions of type Income AND 3 or more transactions of type Expense occurred within a particular time period over a date range for a particular user.
So for example, my search could have the following variables:
DECLARE #StartDate DATETIME = '2018-06-30'
DECLARE #EndDate DATETIME = '2019-09-30'
DECLARE #SearchPeriodMonths INT = 6
My current solution searches in blocks of 6 months (as defined by #SearchPeriodMonths) from #StartDate to #EndDate and if 3 or more transactions of Income and Expense exist, then this would need to be returned in a table. This solution is not very efficient and involves using a cursor. I'm hoping there is a better way of going about it.
CREATE TABLE #Results
(
UserID int,
StartDate DATETIME,
EndDate DATETIME,
Message VARCHAR(255)
)
DECLARE #UserID INT
DECLARE crUser CURSOR FOR
SELECT UserID
FROM Users
OPEN crUser
FETCH NEXT FROM crUser INTO #UserID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #DateCount TABLE(UserID INT, StartDate DATETIME, EndDate DATETIME, IncomeCount INT, ExpenseCount INT)
WHILE(#StartDate < #EndDate)
BEGIN
SELECT #StartDate = DATEADD(DAY, 1, #StartDate)
INSERT INTO #DateCount
SELECT
#UserID AS UserID,
#StartDate AS StartDate,
DATEADD(MONTH, #SearchPeriodMonths, #StartDate) AS EndDate,
(SELECT COUNT(*) FROM Trans WHERE TransTypeID = 1 AND UserID = #UserID
AND TransDate BETWEEN #StartDate and DATEADD(MONTH, #SearchPeriodMonths, #StartDate)
AS IncomeCount,
(SELECT COUNT(*) FROM Trans WHERE TransTypeID = 2 AND UserID = #UserID
AND TransDate BETWEEN #StartDate and DATEADD(MONTH, #SearchPeriodMonths, #StartDate)
AS ExpenseCount
END
INSERT INTO #Results
SELECT
#UserID AS UserID,
MIN(StartDate) AS StartDate,
MAX(EndDate) AS EndDate,
'3 or more Income and Expense transaction types occur in a 6 month period between '
+ convert(varchar, min(StartDate), 106) + ' and '
+ convert(varchar, max(EndDate), 106) AS WarningMessage
FROM #DateCount
WHERE IncomeCount > 2 AND ExpenseCount > 2
FETCH NEXT FROM crUser INTO #UserID
END
CLOSE crUser
DEALLOCATE crUser
-- Output query
SELECT *
FROM #Results
END
I am restricted to doing this in SQL Server 2008 R2. Is there a more efficient way to do this which doesn't involve using a cursor?
I'm looking to find out if 3 or more transactions of type Income AND 3 or more transactions of type Expense occurred within a particular time period over a date range for a particular user.
Use aggregation!
select userid
from trans
where transdate between #startdate and dateadd(month, #SearchPeriodMonths, #StartDate)
group by userid
having sum(case when transtypeid = 1 then 1 else 0 end) >= 3 and
sum(case when transtypeid = 2 then 1 else 0 end) >= 3;

Use cross apply function

I have this cross apply query and I want to sum the result of it
CROSS APPLY
(SELECT
SUM(CASE WHEN [day] BETWEEN #FirstDay AND #LastDay
THEN 1 ELSE 0
END) AS UsedDays
FROM
Calendar c
WHERE
([day] >= r.[DateFrom]
AND [day] <= r.[DateTo]
AND [WorkDay] = 1)) calculateUsedDays
I have a request table that contains different requests from different people
and my point is to sum all of the days from the requests from a person.
The cross apply returns the sum of the days by every request from the requests table for every person.
For example:
person
John, usedDays - 5
John, - 7
Peter - 10
Peter - 5
..
And I want to sum these days and group by name of the person so that I can have all of the days by person.
Example:
John - 12
Peter - 15
I tried with sum and group by , but it returns error:
Each GROUP BY expression must contain at least one column that is not an outer reference
Thank you :))
Thank you guys,
I solved this, but now my problem is: Implicit conversion from data type datetime to int is not allowed. Use the CONVERT function to run this query.
This is my code
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [core].[ReportBalanceYearSearch]
#Year int = NULL,
#TypeOfLeaveGid int = NULL,
#IsActive int = NULL
AS
BEGIN
SET NOCOUNT ON
DECLARE #Err int
DECLARE #sql nvarchar (max), #paramlist nvarchar(max)
DECLARE #FirstDay datetime
DECLARE #LastDay datetime
DECLARE #typeLeaveGid INT, #typeCreditGid INT, #relLeaveToCreditGid INT
SET #FirstDay = DATEFROMPARTS ( #Year, 1, 1)
SET #LastDay = DATEFROMPARTS ( #Year, 12, 31)
SELECT #typeLeaveGid = gid FROM Nomenclature WHERE type = 'RequestType' and Code = 1
SELECT #typeCreditGid = gid FROM Nomenclature WHERE type = 'RequestType' and Code = 2
SELECT #relLeaveToCreditGid = gid FROM Nomenclature WHERE type = 'Relation' and Code = 6
SELECT #sql = '
SELECT u.[Name],
u.[DepartmentGid],
sum(calculateUsedDays.UsedDays - isnull(calculateCreditDays.CreditDaysInPeriod,0)) as [UsedDays],
ub.[Balance],
sum(calculateUsedDays.UsedDays - isnull(calculateCreditDays.CreditDaysInPeriod,0)) + ub.[Balance] as [TotalDaysInYear],
r.[LeaveTypeGid]
FROM [dbo].[Request] r
inner join [User] u on r.[UserGid] = u.[Gid]
inner join [UserBalance] ub on r.[UserGid] = ub.UserGid and ub.Year = #xYear
LEFT OUTER JOIN dbo.Request CRD
inner join Relations rel ON rel.RelID2 = CRD.Gid AND rel.RelType = #xrelLeaveToCreditGid
inner join Nomenclature nsc ON nsc.Gid = CRD.StatusGid
cross apply (SELECT
sum(case when [day] between COALESCE(#xFirstDay, [day]) AND COALESCE(#xLastDay, [day]) then 1 else 0 end) as CreditDaysInPeriod
FROM Calendar c
WHERE [day] >= crd.[DateFrom] AND [day] <= crd.[DateTo] AND [WorkDay] = 1 ) calculateCreditDays
ON rel.RelID1 = r.Gid
and CRD.TypeGid = #xtypeCreditGid
cross apply (SELECT
sum(case when [day] between #xFirstDay and #xLastDay then 1 else 0 end) as UsedDays
FROM Calendar c
WHERE ([day] >= r.[DateFrom] AND [day] <= r.[DateTo] AND [WorkDay] = 1))calculateUsedDays
where #xYear = DATEPART(year,r.[DateFrom]) and r.TypeGid = #xtypeLeaveGid and #xIsActive IS NULL OR u.[Active] = #xIsActive
group by u.[Name], u.[DepartmentGid],r.[LeaveTypeGid], ub.[Balance]'
SELECT #paramlist ='
#xTypeOfLeaveGid int,
#xFirstDay datetime,
#xYear int,
#xLastDay datetime,
#xtypeLeaveGid int,
#xrelLeaveToCreditGid int,
#xtypeCreditGid int,
#xIsActive bit'
EXEC sys.sp_executesql #sql, #paramlist,
#TypeOfLeaveGid,
#Year,
#IsActive,
#typeLeaveGid,
#relLeaveToCreditGid,
#typeCreditGid,
#FirstDay,
#LastDay
SET #Err = ##Error
RETURN #Err
END

Trigger did not run?

I have a trigger "after insert/update/delete/". It is supposed to count Balance on Account table based on transactions in Transaction table. It is on Transaction table. I am getting Balance discrepancies rarely, so have decided to add some logging into it. It dumps inserted+deleted tables (they are combined into a table var) and tsql statement which fired it. Judging from my log, it looks like the trigger did not fire for some inserts into Transaction table. Can this happen ? Are there any TSQL statement which change table data without firing trigger (except truncate table etc)?
Here is the trigger :
CREATE TRIGGER [dbo].[trg_AccountBalance]
ON [dbo].[tbl_GLTransaction]
AFTER INSERT, UPDATE, DELETE
AS
set nocount on
begin try
declare #OldOptions int = ##OPTIONS
set xact_abort off
declare #IsDebug bit = 1
declare #CurrentDateTime datetime = getutcdate()
declare #TriggerMessage varchar(max), #TriggerId int
if #IsDebug = 1
begin
select #TriggerId = isnull(max(TriggerId), 0) + 1
from uManageDBLogs.dbo.tbl_TriggerLog
declare #dbcc_INPUTBUFFER table(EventType nvarchar(30), Parameters Int, EventInfo nvarchar(4000) )
declare #my_spid varchar(20) = CAST(##SPID as varchar(20))
insert #dbcc_INPUTBUFFER
exec('DBCC INPUTBUFFER ('+#my_spid+')')
select #TriggerMessage = replace(EventInfo, '''', '''''') from #dbcc_INPUTBUFFER
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
declare #Oper int
select #Oper = 0
-- determine type of sql statement
if exists (select * from inserted) select #Oper = #Oper + 1
if exists (select * from deleted) select #Oper = #Oper + 2
if #IsDebug = 1
begin
select #TriggerMessage = '#Oper = ' + convert(varchar, #Oper)
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
if #Oper = 0 return -- No data changed
declare #TomorrowDate date = dateadd(day, 1, convert(date, getdate()))
declare #CurrentDate date = convert(date, getdate())
-- transactions from both inserted and deleted tables
declare #tbl_Trans table (FirmId int, GLAccountId int,
AmountDebit money, AmountCredit money, "Status" char(1), TableType char(1))
declare #tbl_AccountCounters table (FirmId int, GLAccountId int, Balance money)
declare #IsChange bit = null
insert into #tbl_Trans (FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", TableType)
select FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", 'I'
from inserted
union
select FirmId, GLAccountId, AmountDebit, AmountCredit, "Status", 'D'
from deleted
if #IsDebug = 1
begin
select #TriggerMessage = (select * from #tbl_Trans for xml path ('tbl_Trans'))
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
insert into #tbl_AccountCounters (FirmId, GLAccountId, Balance)
select FirmId, GLAccountId, 0
from #tbl_Trans
group by FirmId, GLAccountId
if #Oper = 1 or #Oper = 2 -- insert/delete
begin
update #tbl_AccountCounters
set Balance = cnt.TransSum
from #tbl_AccountCounters as ac join
(
select trans.FirmId, trans.GLAccountId,
isnull(sum((trans.AmountDebit - trans.AmountCredit) * iif(trans.TableType = 'I', 1, -1)), 0) as TransSum
from #tbl_Trans as trans
where trans.Status = 'A'
group by trans.FirmId, trans.GLAccountId
) as cnt on ac.FirmId = cnt.FirmId and ac.GLAccountId = cnt.GLAccountId
select #IsChange = 1
end
else
begin
if update(AmountDebit) or update(AmountCredit) or update(Status) or update(GLAccountId)
begin
update #tbl_AccountCounters
set Balance = cnt.TransBalance
from #tbl_AccountCounters as ac join
(select trans.FirmId, trans.GLAccountId, isnull(sum(trans.AmountDebit - trans.AmountCredit), 0) as TransBalance
from dbo.tbl_GLTransaction as trans
where trans."Status" = 'A' and exists (select 1 from #tbl_AccountCounters as ac
where ac.GLAccountId = trans.GLAccountId and ac.FirmId = trans.FirmId)
group by trans.FirmId, trans.GLAccountId) as cnt on
ac.FirmId = cnt.FirmId and ac.GLAccountId = cnt.GLAccountId
select #IsChange = 0
end
end
if #IsDebug = 1
begin
select #TriggerMessage = '#IsChange = ' + isnull(convert(varchar, #IsChange), 'null')
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
select #TriggerMessage = (select * from #tbl_AccountCounters for xml path ('tbl_AccountCounters'))
insert into uManageDBLogs.dbo.tbl_TriggerLog (TriggerId, "Message", CreateDate)
values (#TriggerId, #TriggerMessage, #CurrentDateTime)
end
if #IsChange is not null
begin
update tbl_GLAccount
set tbl_GLAccount.Balance = iif(#IsChange = 1, cnt.Balance + acc.Balance, cnt.Balance),
tbl_GLAccount.LastUpdate = getutcdate(),
tbl_GLAccount.LastUpdatedBy = 1
from #tbl_AccountCounters as cnt join dbo.tbl_GLAccount as acc on
cnt.FirmId = acc.FirmId and cnt.GLAccountId = acc.GLAccountId
end
if (16384 & #OldOptions) = 16384 set xact_abort on
end try
begin catch
declare #ErrorLine varchar(max)
select #ErrorLine = uManageDb.dbo.udf_GetErrorInfo()
insert into uManageDb.dbo.tbl_TriggerError ("Name", "Message", CreateDate)
values ('AccountingDB..trg_AccountBalance', #ErrorLine, GETUTCDATE())
end catch
I think I've found it. I have this line:
select .. from inserted
union
select .. from deleted
and they inserted 5 trans for $300 and 4 trans $100. I've got 2 records (300 and 100) in my #tbl_Trans (it was in the log). That's probably was the bug. So log hellps and trigger run as it had to.
I'll replace union with union all.

Cursor poor performance issue

I have a query which runs very slow. After some observations, I feel that it is because of using cursor in the query.
CREATE PROCEDURE [dbo].[spTest] (#INwww varchar(6)) AS
declare #curwwwbegdate datetime
declare #prevwwwbegdate datetime
declare #PrevwwwNum varchar(6)
set NOCOUNT ON
set #CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = #Inwww)
set #PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < #curwwwbegdate
order by begindate desc)
CREATE TABLE #ContRevExp (Inum int, lwww varchar(6),BookNum varchar(13), ldate datetime, lllLoc varchar(8), currentrev varchar(20), currentexp varchar(20), oldrev varchar(20), oldexp varchar(20), ContFileLoc varchar(100), bookloc varchar(3))
create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
create table #LocTable (loc varchar(3))
insert into #LocTable
SELECT LocCode from Location WHERE STATUS = 1 AND CCC = 1
DECLARE LocCursor INSENSITIVE CURSOR FOR
select * from #LocTable
OPEN LocCursor
declare #Loc varchar(3)
FETCH NEXT FROM LocCursor into #Loc
WHILE (##FETCH_STATUS = 0)
BEGIN
insert into #wwwdates (wdLoc, wdwww, wdBegDate, wdEndDate)
select #Loc,#Inwww,
(select top 1 wwwllldate from cccswwwllled where lllwww = #PrevwwwNum and branch = #Loc),
(select top 1 wwwllldate from cccswwwllled where lllwww = #Inwww and branch = #Loc)
FETCH NEXT FROM LocCursor into #Loc
END
deallocate LocCursor
Drop table #LocTable
DECLARE LocwwwCursor INSENSITIVE CURSOR FOR
select wdLoc, wdBegDate, wdEndDate from #wwwdates
declare #INFILELOC as varchar(3)
declare #INBEGDATE datetime
declare #INENDDATE datetime
OPEN LocwwwCursor
FETCH NEXT FROM LocwwwCursor into #INFILELOC, #INBEGDATE, #INENDDATE
WHILE (##FETCH_STATUS = 0)
BEGIN
declare #INVNUM int
declare #INDATE datetime
create table #cccs (cInvNum int)
insert into #cccs
Select distinct cccInv_Num from cccswwwllled
left join cccsexport on cccsExport.inv_num = cccinv_num
where cccswwwllled.Branch = #INFILELOC
and (cccswwwllled.impexp <> 1 or cccswwwllled.impexp is null)
and lllwww < #INwww and ContBookIndicator = 'C'
and cccsexport.lastupdate >= #INBEGDATE and cccsexport.lastupdate < #INENDDATE
DECLARE cccCursor INSENSITIVE CURSOR FOR
select * from #cccs
OPEN cccCursor
declare #cnum int
FETCH NEXT FROM cccCursor into #cnum
WHILE (##FETCH_STATUS = 0)
BEGIN
insert into #ContRevExp (Inum , lwww ,BookNum, ldate , lllloc , currentrev , currentexp, oldrev, oldexp, contfileloc, bookloc )
select top 1 cccInv_Num, lllwww,
(Select top 1 cccNumber from cccsExport where inv_num = #cnum and lastupdate <= #INENDDATE order by lastupdate desc ),
wwwlllDate,CE.FileNum,
0,
--case when CE.STATUS = 0 then '0' else convert(varchar(20),CE.TotalCost + CE.AllinRpoCost) end,
case when CE.STATUS = 0 then '0' else convert(varchar(20),CE.TotalExpense) end,
0,
--(Select top 1 convert(varchar(20),TotalCost + Allinrpocost) from cccsExport where inv_num = #cnum and lastupdate <= #INBEGDATE order by lastupdate desc ),
(Select top 1 convert(varchar(20),TotalExpense) from cccsExport where inv_num = #cnum and lastupdate <= #INBEGDATE order by lastupdate desc ),
'Cont/File/Loc',
BookLocation from cccswwwllled as CWL
left join cccsexport as CE on CE.inv_num = CWL.cccInv_Num
where CWL.cccInv_Num = #cnum
and CWL.lllwww < #INwww and CWL.ContBookIndicator = 'C'
and CE.lastupdate >= #INBEGDATE and CE.lastupdate <= #INENDDATE
order by CE.lastupdate desc
FETCH NEXT FROM cccCursor into #cnum
END
deallocate cccCursor
drop table #cccs
FETCH NEXT FROM LocwwwCursor into #INFILELOC, #INBEGDATE, #INENDDATE
END
deallocate LocwwwCursor
drop table #wwwdates
select bookloc,lwww,booknum,Inum,oldrev,oldexp,currentrev,currentexp,lllloc,
case when contfileloc is null then 'NOT' ELSE contfileloc end as 'Cont' from #ContRevExp
where (currentrev <> oldrev) or (currentexp <> oldexp)
order by bookloc, booknum
drop table #ContRevExp
I am not strong on cursor. My question is how to modify the code to improve the performance?
EDIT
The real problem is that I use ado.net to retrieve the data in a web application, the entire time to download data from server to the client is about 10 seconds, By using telerik justtrace tool, I found the stored procedure spent 99% time.
In the query, I found there is a nest cursors usage. cccCursor and LocwwwCursor. It means that there are two while loops, one is inside the other one. I guess that is the main reason, so I hope to replace the cursors with other skills.
SAMPLE DATA:
Because there are a lot of tables. I try to make the logic clear. We get the data from table Location:
The temp table #LocTable only has one column. The sample data is
AAA
BBB
CCC
DDD
EEE
FFF
GGG
I believe LocCursor is dealing each value of above data.
The input of the stored procedure is Inwww, let say it is 200218.
We have the query set #CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = #Inwww) will return a datetime 2002-04-28 00:00:00.000.
Another query set #PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < #curwwwbegdate
order by begindate desc) will return 200217. The temp table #wwwdates has the sample data as below.
wdLoc wdWeek wdBegDate wdEndDate
AAA 200218 NULL NULL
BBB 200218 NULL NULL
CCC 200218 NULL NULL
DDD 200218 2002-05-06 13:39:31.000 2002-05-07 16:52:44.000
EEE 200218 NULL NULL
FFF 200218 2002-05-06 13:39:40.000 2002-05-07 16:53:42.000
GGG 200218 NULL NULL
I think that the cursor LocwwwCursor takes the value from the temp table #wwwdatae then insert #cccs. I haven't got the sample data yet.
The final result likes(I just demonstrate it with one row)
ARL ARL 200510 IETARL0510087 150547816 $0.00 155.21 $0.00 155.00 ARL SOMETHING
There are unnecessary loops for aggerates in the SP above. Most of your logic could be condensed and made more performant with grouping and aggregations. For example, The whole block below could be drastically simplified.
REPLACE THIS ....
declare #prevwwwbegdate datetime
declare #PrevwwwNum varchar(6)
set NOCOUNT ON
set #CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = #Inwww)
set #PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < #curwwwbegdate
order by begindate desc)
CREATE TABLE #ContRevExp (Inum int, lwww varchar(6),BookNum varchar(13), ldate datetime, lllLoc varchar(8), currentrev varchar(20), currentexp varchar(20), oldrev varchar(20), oldexp varchar(20), ContFileLoc varchar(100), bookloc varchar(3))
create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
create table #LocTable (loc varchar(3))
insert into #LocTable
SELECT LocCode from Location WHERE STATUS = 1 AND CCC = 1
DECLARE LocCursor INSENSITIVE CURSOR FOR
select * from #LocTable
OPEN LocCursor
declare #Loc varchar(3)
FETCH NEXT FROM LocCursor into #Loc
WHILE (##FETCH_STATUS = 0)
BEGIN
insert into #wwwdates (wdLoc, wdwww, wdBegDate, wdEndDate)
select #Loc,#Inwww,
(select top 1 wwwllldate from cccswwwllled where lllwww = #PrevwwwNum and branch = #Loc),
(select top 1 wwwllldate from cccswwwllled where lllwww = #Inwww and branch = #Loc)
FETCH NEXT FROM LocCursor into #Loc
END
deallocate LocCursor
Drop table #LocTable
WITH THIS ....
create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
insert into #wwwdates
SELECT
LocCode,#Inwww,MAX(Prev_MaxDate),MAX(In_MaxDate)
FROM
(
SELECT LocCode,
Prev_MaxDate=CASE WHEN cccswwwllled.lllwww=#PrevwwwNum THEN wwwllldate ELSE NULL END,
In_MaxDate=CASE WHEN cccswwwllled.lllwww=#Inwww THEN wwwllldate ELSE NULL END
from
Location
INNER JOIN cccswwwllled ON cccswwwllled.lllwww IN(#PrevwwwNum,#Inwww) AND cccswwwllled.Branch=Location.LocCode
WHERE Location.STATUS = 1 AND Location.CCC = 1
)AS X
GROUP BY
LocCode
You should remove cursors and create set-based solution. You and mssql engine are not strong in cursor solutions :).
Rething what you would like to do. Join wwwNUM + lllwww + Location into view something like + check indexes on wwwnumber, branch, LocCode + STATUS + CCC (depends on data types)
create view MyViewWithNameWhichDescriptContain
as
select
l.LocCode,
n.wwwNUMBER Inwww,
n.Begindate InwwwDate,
c.wwwllldate Inwww
from Location l, wwwNUM n, cccswwwllled c
where l.[STATUS] = 1 AND l.CCC = 1
and l.LocCode = c.branch
and c.lllwww = n.wwwnumber
select from this view:
if object_id('tempdb..#wwwMyDatesPerLocCode') is not null drop table #wwwMyDatesPerLocCode
select *
into #wwwMyDatesPerLocCode
from (
select *, row_number() over(partition by Inwww order by InwwwDate desc OrbyCol
from MyViewWithNameWhichDescriptContain) t
where OrbyCol in (1,2)
create view cccswwwllled + cccsexport lets say llledExported
make selfjoin to access descendant (wwwMyDatesPerLocCode where OrbyCol = 1 + wwwMyDatesPerLocCode where OrbyCol = 2) and connect with view llledExported to get your info with your filters
check indexes on all tables again
Finnaly you don't need variables and cursors. Try think in joins. If you will add some somple data from all tables and add description of your indexes I can make it more detailed.

How to fix this logic?

i have a query with 3 variable tables: #result, #order and #stock.
the logic is the stock qty must be allocated by lotsize (here i set=1) to all order based on priority (FIFO). the stock qty must be allocated till zero and the allocateqty must <= orderqty. the problem is one of orders, its allocateqty is over orderqty (priority=7) while other are correct.
DECLARE #RESULT TABLE (priority int,partcode nvarchar(50),orderqty int, runningstock int, allocateqty int)
DECLARE #ORDER TABLE(priority int,partcode nvarchar(50),orderqty int)
DECLARE #STOCK TABLE(partcode nvarchar(50),stockqty int)
INSERT INTO #ORDER (priority,partcode,orderqty)
VALUES (1,'A',10),
(2,'A',50),
(3,'A',10),
(4,'A',40),
(5,'A',3),
(6,'A',5),
(7,'A',11),
(8,'A',10),
(9,'A',10),
(10,'A',10);
INSERT INTO #STOCK(partcode,stockqty)
VALUES('A',120)
IF (SELECT SUM(orderqty)FROM #ORDER)<(SELECT stockqty FROM #STOCK)
BEGIN
INSERT INTO #RESULT(priority,partcode,orderqty,allocateqty)
SELECT priority, partcode,orderqty,orderqty
FROM #ORDER
END
ELSE
BEGIN
DECLARE #allocatedqty int = 0
DECLARE #Lotsize int=1
DECLARE #allocateqty int = #Lotsize
DECLARE #runningstock int = (SELECT stockqty FROM #stock)
WHILE #runningstock>=0
BEGIN
DECLARE #priority int
SELECT TOP 1 #priority = priority FROM #order ORDER BY priority ASC
WHILE #priority <= (SELECT MAX(priority) FROM #order)
BEGIN
DECLARE #orderqty int
SELECT #orderqty = orderqty - #allocatedqty FROM #order WHERE priority = #priority
SELECT #allocateqty = CASE WHEN #runningstock > 0 AND #orderqty > 0 THEN #Lotsize ELSE 0 END
INSERT INTO #RESULT(priority,partcode,orderqty,runningstock,allocateqty)
SELECT #priority,
partcode,
CASE WHEN #orderqty >= 0 THEN #orderqty ELSE 0 END AS orderqty,
#runningstock,
#allocateqty
FROM #order
WHERE priority = #priority
SET #priority += 1
SET #runningstock = #runningstock - #allocateqty
END
SET #allocatedqty += #allocateqty
IF (#runningstock <= 0) BREAK
END
END
select * from #RESULT where priority=7;
SELECT priority,
sum(allocateqty) as allocated
from #RESULT
group by priority
the result:
my reputation not reach 50 so cant add comment.
you said your other order is correct then priority = 7 is also correct. you can compare priority 2 and 4 with 7. its the same thing. i think all of your loop for orderqty only reach 10 times where priority 7 got 11 so it will left 1.
Either everything is correct or everything is wrong =x
EDIT:
Hi, I found the answer.
Change
SET #allocatedqty += #allocateqty
to
SET #allocatedqty += 1
because when using SET #allocatedqty += #allocateqty, the last order #allocateqty is 0 then it will always make #allocatedqty = 0 then it will not increase.
Hope this really help you.
EDIT based on #Jesuraja given answer it should be:
SET #allocatedqty += #Lotsize
As I'm not quite sure what you try to achieve with records which will set your stock to 0 or beyond I just can provide this. But it is much better than to run all your orders in a loop. Maybe you'll want to replace your loop.
DECLARE #RESULT TABLE (priority int,partcode nvarchar(50),orderqty int, runningstock int, allocateqty int)
DECLARE #ORDER TABLE(priority int,partcode nvarchar(50),orderqty int)
DECLARE #STOCK TABLE(partcode nvarchar(50),stockqty int)
INSERT INTO #ORDER (priority,partcode,orderqty)
--VALUES (1,'A',10),(2,'A',50),(3,'A',10),(4,'A',40),(5,'A',3),(6,'A',5),(7,'A',11),(8,'A',10),(9,'A',10),(10,'A',10); --your orders
VALUES (1,'A',1),(2,'A',2),(3,'A',3),(4,'A',4),(5,'A',5),(6,'A',6),(7,'A',7),(8,'A',8),(9,'A',9),(10,'A',10);
INSERT INTO #STOCK(partcode,stockqty)
--VALUES('A',50) -- your stock
VALUES('A',50)
IF (SELECT SUM(orderqty) FROM #ORDER)<(SELECT stockqty FROM #STOCK)
BEGIN
INSERT INTO #RESULT(priority,partcode,orderqty,allocateqty)
SELECT priority, partcode,orderqty,orderqty
FROM #ORDER
END
ELSE
BEGIN
;WITH dat AS(
SELECT s.partcode, s.stockqty, o.priority, o.orderqty,
ROW_NUMBER() OVER(PARTITION BY s.partcode ORDER BY o.priority DESC) as runningOrder
FROM #Stock as s
INNER JOIN #ORDER as o
ON s.partcode = o.partcode
)
INSERT INTO #RESULT(priority,partcode,orderqty,runningstock,allocateqty)
SELECT d1.priority, d1.partcode, d1.orderqty,
d1.stockqty - SUM(d2.orderqty) OVER(PARTITION BY d1.runningOrder) as runningstock,
CASE WHEN d1.stockqty - SUM(d2.orderqty) OVER(PARTITION BY d1.runningOrder) > 0 AND d1.orderqty > 0 THEN 1 ELSE 0 END
FROM dat as d1
INNER JOIN dat as d2
ON d1.partcode = d2.partcode
AND d1.runningOrder >= d2.runningOrder
END
select * from #RESULT where priority=7;
SELECT priority,
sum(allocateqty) as allocated
from #RESULT
group by priority

Resources