How to make this code more professional look? - sql

DECLARE #Coid INT
DECLARE #DTRID INT
DECLARE #EMPID INT
DECLARE #DATE datetime
SELECT TOP 1 #EMPID = tblEmployees.Id, #Coid = tblEmployees.CompanyId, #DATE = tblDailyTimeRecord.TimeIn, #DTRID = tblDailyTimeRecord.Id FROM tblEmployees INNER JOIN tblDailyTimeRecord ON tblEmployees.Id = tblDailyTimeRecord.EmployeeId WHERE tblDailyTimeRecord.Date = CONVERT(date, GETDATE()) AND tblDailyTimeRecord.TimeOut IS NULL ORDER BY tblDailyTimeRecord.ID DESC
IF #Coid IS NULL
BEGIN
DECLARE #IdentityValue TABLE ( ContactID int,EmpId int,DATE datetime)
INSERT INTO [tblDailyTimeRecord]([EmployeeId],[Date],[TimeOut],[IsModified],[CompanyId])
OUTPUT INSERTED.Id,INSERTED.EmployeeId,INSERTED.TimeOut INTO #IdentityValue
Select Id,CONVERT(date, GETDATE()),GETDATE(),0,CompanyId From tblEmployees Where AccessCode = 'GI0056'
INSERT INTO tblTimeLog([EmployeeId],[Time],[Type],[TimeLogSourceId],[CreationDate])
Select EmpId,DATE,2,11,GETDATE() From #IdentityValue
END
ELSE
BEGIN
DECLARE #PAIR int
DECLARE #TIMEOUT datetime
SET #TIMEOUT = GETDATE()
IF #DATE IS NULL
SET #PAIR = 0
ELSE
SET #PAIR = 1
UPDATE [tblDailyTimeRecord] SET TimeOut = #TIMEOUT, PairNo = #PAIR WHERE ID = #DTRID
INSERT INTO tblTimeLog([EmployeeId],[Time],[Type],[TimeLogSourceId],[CreationDate])
VALUES (#EMPID,#TIMEOUT,2,11,#TIMEOUT)
END

Just general formatting rules:
1. All declaration better are in the beginning.
2. Be consistent: all reserved words in capital
3. Do not use reserved words for column names.
4. Comment your code.
5. Use unified intends.
6. Finish statements with semicolon.
7. If you can, use CASE instead of IF SET.
8. Use short aliases for tables and views.
9. Name variables in SELECT statement, then it will be clear what goes where.
Have I forgot something?
Logic/Design.
1. Instead of multiple variables you could use temp table variable or just temp table. It would make code shorter and more readable.
2. First SELECT returns one row, but in the INSERT statement you might insert something completely irrelevant.
3. Having the same data in two tables might be a sign of bad design.
4. Having Date and Datetime for the same event in the same row is very suspicious.
It might be as the code below. Hope that helps.
DECLARE #Coid INT;
DECLARE #DTRID INT;
DECLARE #EMPID INT;
DECLARE #DATE DATETIME;
DECLARE #PAIR INT;
DECLARE #TIMEOUT DATETIME;
DECLARE #IdentityValue TABLE (
ContactID int,
EmpId int,
ContactDate DATETIME
);
-- Select ONLY one row
SELECT TOP 1
#EMPID = e.Id,
#Coid = e.CompanyId,
#DATE = tr.TimeIn,
#DTRID = tr.Id,
#TIMEOUT = GETDATE(),
#PAIR = CASE WHEN tr.TimeIn IS NULL THEN 0 ELSE 1 END
FROM tblEmployees as e
INNER JOIN tblDailyTimeRecord as tr
ON e.Id = tr.EmployeeId
WHERE tr.Date = CONVERT(DATE, GETDATE())
AND tr.TimeOut IS NULL
ORDER BY tr.ID DESC;
IF #Coid IS NULL
BEGIN
-- Insert more than one row or might insert nothing
INSERT INTO [tblDailyTimeRecord] (
[EmployeeId],
[Date],
[TimeOut],
[IsModified],
[CompanyId]
)
OUTPUT INSERTED.Id,
INSERTED.EmployeeId,
INSERTED.TimeOut
INTO #IdentityValue
SELECT Id as [EmployeeId],
CONVERT(DATE, GETDATE()) as [Date],
#TIMEOUT as [TimeOut],
0 as [IsModified],
CompanyId
FROM tblEmployees
WHERE AccessCode = 'GI0056';
INSERT INTO tblTimeLog(
[EmployeeId],
[Time],
[Type],
[TimeLogSourceId],
[CreationDate])
SELECT EmpId as [EmployeeId],
ContactDate as [Time],
2 as [Type],
11 as [TimeLogSourceId],
#TIMEOUT as [CreationDate]
FROM #IdentityValue;
END
ELSE
BEGIN
UPDATE [tblDailyTimeRecord]
SET TimeOut = #TIMEOUT,
PairNo = #PAIR
WHERE ID = #DTRID
INSERT INTO tblTimeLog(
[EmployeeId],
[Time],
[Type],
[TimeLogSourceId],
[CreationDate]
) VALUES (
#EMPID,
#TIMEOUT,
2,
11,
#TIMEOUT
);
END

Related

Iterate inserted in trigger

This SQL Server trigger works correctly for a single insert
CREATE TRIGGER [dbo].[DebitSale]
ON [dbo].[OrderDetails]
AFTER INSERT, UPDATE
AS
DECLARE #OrderId bigint
DECLARE #OrderNr int
DECLARE #TaxRate decimal(9,2)
DECLARE #Tax decimal(9,2)
DECLARE #Total decimal(9,2)
DECLARE #syncRate decimal(9,8)
DECLARE #Net decimal(9,2)
DECLARE #OldNet decimal(9,2)
DECLARE #ExactFee decimal(9,2)
DECLARE #Fee decimal(9,2)
DECLARE #JobId bigint
DECLARE #ItemCode nvarchar(35)
IF EXISTS ( SELECT 0 FROM inserted )
BEGIN
SELECT #syncRate = SyncRate
FROM dbo.[UserAccount]
WHERE Id = 0
SELECT #Net = i.NrUnits * i.UnitNet from inserted i
SELECT #OldNet = d.NrUnits * d.UnitNet from deleted d
IF(#Net=#OldNet)
BEGIN
RETURN
END
SELECT #OrderId = i.OrderId, #JobId = i.JobId, #ItemCode = i.ItemCode FROM inserted i
SELECT #OrderNr = OrderNr, #TaxRate = TaxRate FROM Orders WHERE OrderId = #OrderId
SET #Tax = #Net * #TaxRate / 100
SET #Total = #Net + #Tax
SET #ExactFee = #Total * #syncRate
SET #Fee = FLOOR(#ExactFee * 100)/100
IF(#Fee >= 0.01)
BEGIN
IF #JobId IS NULL
INSERT INTO SubscriptionTransactions
(ItemValue, OrderNr, TransactionCredit, TransactionDate, TransactionDebit, TransactionDescription, TransactionType)
VALUES (#Total, #OrderNr, 0, GetDate(), #Fee, #ItemCode, 'Item')
ELSE
INSERT INTO SubscriptionTransactions
(ItemValue, OrderNr, TransactionCredit, TransactionDate, TransactionDebit, TransactionDescription, TransactionType)
VALUES (#Total, #OrderNr, 0, GetDate(), #Fee, #ItemCode, 'Job')
END
END
However we sometimes get multiple rows in inserted so I included a loop like this
ALTER TRIGGER [dbo].[DebitSale]
ON [dbo].[OrderDetails]
AFTER INSERT, UPDATE
AS
BEGIN
DECLARE #OrderId bigint
DECLARE #OrderDetailId bigint
DECLARE #OrderNr int
DECLARE #TaxRate decimal(9,2)
DECLARE #Tax decimal(9,2)
DECLARE #Total decimal(9,2)
DECLARE #syncRate decimal(9,8)
DECLARE #Net decimal(9,2)
DECLARE #OldNet decimal(9,2)
DECLARE #ExactFee decimal(9,2)
DECLARE #Fee decimal(9,2)
DECLARE #JobId bigint
DECLARE #ItemCode nvarchar(35)
SELECT #syncRate = SyncRate
FROM dbo.[UserAccount]
WHERE Id = 0
DECLARE #current INT = 0;
DECLARE #InsertedCount int
SELECT #InsertedCount = Count(*) FROM inserted
DECLARE #Thing nvarchar(max)
SELECT #Thing ='InsertedCount ' + CAST(#InsertedCount AS VARCHAR)
INSERT INTO DebugTable (DebugColumn) VALUES (#Thing)
WHILE #current <= #InsertedCount
BEGIN
SELECT #Thing ='current ' + CAST(#current AS VARCHAR)
INSERT INTO DebugTable (DebugColumn) VALUES (#Thing)
SELECT #OrderDetailId AS OrderDetailId from inserted i
ORDER BY OrderDetailId
OFFSET #current ROWS
FETCH NEXT 1 ROWS ONLY
SET #current = #current + 1;
SELECT #Net = NrUnits * UnitNet FROM inserted i WHERE i.OrderDetailId= #OrderDetailId
SELECT #OldNet = d.NrUnits * d.UnitNet from deleted d where d.OrderDetailId= #OrderDetailId
IF(#Net=#OldNet)
CONTINUE
SELECT #OrderId = i.OrderId, #JobId = i.JobId, #ItemCode = i.ItemCode FROM inserted i WHERE i.OrderDetailId= #OrderDetailId
SELECT #OrderNr = OrderNr, #TaxRate = TaxRate FROM Orders WHERE OrderId = #OrderId
SET #Tax = #Net * #TaxRate / 100
SET #Total = #Net + #Tax
SET #ExactFee = #Total * #syncRate
SET #Fee = FLOOR(#ExactFee * 100)/100
IF(#Fee < 0.01)
CONTINUE
IF (#JobId IS NULL)
INSERT INTO SubscriptionTransactions
(ItemValue, OrderNr, TransactionCredit, TransactionDate, TransactionDebit, TransactionDescription, TransactionType)
VALUES (#Total, #OrderNr, 0, GetDate(), #Fee, #ItemCode, 'Item')
ELSE
INSERT INTO SubscriptionTransactions
(ItemValue, OrderNr, TransactionCredit, TransactionDate, TransactionDebit, TransactionDescription, TransactionType)
VALUES (#Total, #OrderNr, 0, GetDate(), #Fee, #ItemCode, 'Job')
END
INSERT INTO DebugTable (DebugColumn) VALUES (#ItemCode)
END
This is a remote server that services a Web API. I don't have access to remote debugging so I've put some debug code in that logs a couple of lines to a DebugTable. The trigger writes to DebugTable if I execute an insert statement in SSMS, but doesn't update Transactions.
The Web API executes a merge statement in which case nothing is written to DebugTable and Transactions are not updated. I'm not sure if the new trigger is even firing.
I know there is a lot of TSQL here, but given the original trigger seems to work for a single insert, and that trigger is unchanged except for wrapping it in a loop, I assume I've got the loop wrong.
I expect some will spurn the loop, but in mitigation, there are rarely more than 2 rows inserted at a time.
How do I loop through inserted in a trigger?
Your main insert should be something like the following i.e. there is no need for a loop. One should always aim for set based operations in SQL if at all possible. The key points are:
JOIN the relevant tables together
use CROSS APPLY to avoid having to repeat calculations
use the WHERE clause to exclude cases you are currently using CONTINUE for.
I've tried to match your logic as best I can. But without being able to test it I can't be sure it works. You will want to run it through a few tests to be sure its working exactly as you wish.
And I leave the debugging/logging code as an exercise for you.
INSERT INTO dbo.SubscriptionTransactions (ItemValue, OrderNr, TransactionCredit, TransactionDate, TransactionDebit, TransactionDescription, TransactionType)
SELECT
T.Total
, O.OrderNr
, 0
, GetDate()
, F.Fee
, I.ItemCode
, CASE WHEN I.JobId IS NULL THEN 'Job' ELSE 'Item' END
FROM Inserted I
-- Deleted won't exist for an insert
LEFT JOIN Deleted D on D.OrderDetailId = I.OrderDetailId
INNER JOIN Orders O on O.OrderId = I.OrderId
CROSS APPLY (
VALUES (I.NrUnits * I.UnitNet, D.NrUnits * D.UnitNet)
) N (Net, OldNet)
CROSS APPLY (
VALUES (N.Net * (1.0 + N.Net * O.TaxRate / 100))
) T (Total)
CROSS APPLY (
VALUES (FLOOR(T.Total * #SyncRate * 100)/100)
) F (Fee)
WHERE (N.Net != N.OldNet OR D.OrderDetailId IS NULL) -- First continue condition
AND Fee >= 0.01; -- Second continue condition

SQL While Loop Insert with values from another table

I am trying to create a SQL While loop that will update a temp table with values from another table. Values from the other table:
477286
560565
499330
391827
127375
526354
501736
357359
410433
500946
261297
377667
135931
235691
247239
143672
548752
471945
...
Wrote the following, however, it only inserts the last value multiple times over.
Here is the code:
USE Reports
GO
CREATE TABLE #TempTable (CreatedByID int, LastUpdatedByID int, ID int,
AlertDE int, Alert char(50), StartDTTM datetime, EndDTTM datetime,
IsInactiveFLAG char(1),AlertDetails char(1));
DECLARE #numrows INT
SELECT #numrows = COUNT(*) FROM [Reports].[dbo].[Eligible]
DECLARE #id int
DECLARE #LoopCount INT = 1
DECLARE #count int = #numrows
SELECT #id = [id] FROM [Reports].[dbo].[Eligible]
WHILE (#LoopCount <= #count)
BEGIN
INSERT INTO #TempTable (CreatedByID, LastUpdatedByID, ID, AlertDE, Alert, StartDTTM, EndDTTM, IsInactiveFLAG,AlertDetails)
VALUES (52,52,#id,0,'Eligible',CURRENT_TIMESTAMP,'1900-01-01
00:00:00.000','N','')
SET #LoopCount = #LoopCount + 1
END
SELECT * FROM #TempTable
DROP TABLE #TempTable
I am assuming I have to tell it to loop through the values in the other table somehow but I am not positive if that is the right approach or if in general I am taking the long way around the bus.
Why are you using a loop? You can do this with an insert . . . select statement:
INSERT INTO #TempTable (CreatedByID, LastUpdatedByID, ID, AlertDE, Alert, StartDTTM, EndDTTM, IsInactiveFLAG, AlertDetails)
SELECT 52, 52, e.id, 0, 'Eligible', CURRENT_TIMESTAMP, '1900-01-01 00:00:00.000', 'N', ''
FROM [Reports].[dbo].[Eligible] e ;
See eg https://www.w3schools.com/sql/sql_insert_into_select.asp for more info.
GMR, I found a way to accomplish my need which is similar to yours. Hopefully this will help you too.
DECLARE
#LoopId int
,#TheOrderNumber varchar(20)
DECLARE #CheckThisItem TABLE
(
LoopId int not null identity(1,1)
,TheOrderNumber varchar(20) not null
)
INSERT #CheckThisItem
SELECT Order_Number AS TheOrderNumber
FROM [dbo].[Table_Storing_Order_Number] ORDER BY Order_Number ASC
SET #LoopId = ##rowcount
WHILE #LoopId > 0
BEGIN
SELECT #TheOrderNumber = TheOrderNumber
FROM #CheckThisItem
WHERE LoopId = #LoopId
-- Start inserting record pulled for while loop
INSERT [dbo].[The_Destination_Table]
SELECT TOP (1)
A, B, C, D
FROM [dbo].[Source_Table] ST
WHERE
ST.Order_Number = #TheOrderNumber
-- Set number to reduce loop counter
SET #LoopId = #LoopId - 1
END;

Stored Function with Multiple Queries and Different Selected Columns

I have series of queries based on a report type. For simplicity here is an example of what i'm trying to do:
If #Reporttype = '1'
Select lcustomerid, lname, fname
from customers
Where dtcreated > #startdate
Else if #Reporttype = '2'
Select barcode, lname, fname
from employees
where dtcreated > #startdate
Else if #reporttype = '3'
Select thetime, lname, name, barcode, lcustomerid
from Customers
where dtcreated > #startdate
You'll notice that I run 3 separate queries, based on the report type being passed. You'll also notice I am returning different columns and the number of columns.
I'd like to make this a stored function, and return the columns I need based on the report type I pass. However, I know that since the number of columns, and the column names are different - that's not going to work as a stored function as I'd like it to.
The major problem here will be reporting this information - I don't want to have separate functions, because i'll have to maintain different reports for each report type.
Is there a way I can make this work?
You can use multi-statement function but you need to specify all columns which will be returned by 3 select statements. It seems it's impossible return multiple result sets.
User-defined functions can not return multiple result sets. Use a
stored procedure if you need to return multiple result sets. https://msdn.microsoft.com/en-us/library/ms191320.aspx
This is one inconvenience but in report you can use only columns you need, others will be nulls.
CREATE FUNCTION MyFun
(
#Reporttype int,
#startdate datetime
)
RETURNS
#Result TABLE
(
lcustomerid int,
lname nvarchar(50),
fname nvarchar(50),
barcode int,
thetime datetime,
name nvarchar(50)
)
AS
BEGIN
If #Reporttype = '1'
insert into #Result (lcustomerid, lname, fname)
select lcustomerid, lname, fname
from customers
Where dtcreated > #startdate
Else if #Reporttype = '2'
insert into #Result (barcode, lname, fname)
Select barcode, lname, fname
from employees
where dtcreated > #startdate
Else if #reporttype = '3'
insert into #Result (thetime, lname, name, barcode, lcustomerid)
Select thetime, lname, name, barcode, lcustomerid
from customers
where dtcreated > #startdate
RETURN
END
So, you can call function in this way
SELECT * FROM dbo.MyFun (1, getdate())
If you cannot use stored procedure and you need to use a function, you can UNPIVOT the data and than in the client side you can PIVOT it.
I need to do something like this when different number of columns are returned to SQL Server Reporting Services report. For example, the following code is always returning three columns - RowID, Column, Value:
DECLARE #Table01 TABLE
(
[ID] INT
,[Value01] INT
,[Value02] NVARCHAR(256)
,[Value03] SMALLINT
);
DECLARE #Table02 TABLE
(
[ID] INT
,[Value01] INT
);
INSERT INTO #Table01 ([ID], [Value01], [Value02], [Value03])
VALUES (1, 111, '1V2', 7)
,(2, 222, '2V2', 8)
,(3, 333, '3V2', 9);
INSERT INTO #Table02 ([ID], [Value01])
VALUES (1, 111)
,(2, 222)
,(3, 333);
-- your function starts here
DECLARE #Mode SYSNAME = 'Table01' -- try with 'Table02', too
DECLARE #ResultSet TABLE
(
[RowID] INT
,[Column] SYSNAME
,[Value] NVARCHAR(128)
);
IF #Mode = 'Table01'
BEGIN;
INSERT INTO #ResultSet ([RowID], [Column], [Value])
SELECT [ID]
,[Column]
,[Value]
FROM
(
SELECT [ID]
,CAST([Value01] AS NVARCHAR(256))
,CAST([Value02] AS NVARCHAR(256))
,CAST([Value03] AS NVARCHAR(256))
FROM #Table01
) DS ([ID], [Value01], [Value02], [Value03])
UNPIVOT
(
[Value] FOR [Column] IN ([Value01], [Value02], [Value03])
) UNPVT
END;
ELSE
BEGIN;
INSERT INTO #ResultSet ([RowID], [Column], [Value])
SELECT [ID]
,[Column]
,[Value]
FROM
(
SELECT [ID]
,CAST([Value01] AS NVARCHAR(256))
FROM #Table02
) DS ([ID], [Value01])
UNPIVOT
(
[Value] FOR [Column] IN ([Value01])
) UNPVT
END;
SELECT *
FROM #ResultSet;
Then in the reporting I need to perform pivot operation again. This is workaround with many limitations:
the unpivot data must be cast to its largest type (usually, string)
unnecessary operations are performed (pivot -> unpivot) instead of just rendering the data;
it does not working well with large amount of data (it is slow)
and others..
For This you may create a scalar value function that return an xml type column and then you can populate that xml tag values to your report screen
CREATE FUNCTION ReportFunc
(
#intReporttype int,
#dtStartdate datetime
)
RETURNS XML
BEGIN
Declare #xmlResult xml
If #intReporttype = '1'
SET #xmlResult = (
select lcustomerid, lname, fname
from customers
Where dtcreated > #dtStartdate
FOR XML PATH (''), TYPE
)
Else if #intReporttype = '2'
SET #xmlResult = (
Select barcode, lname, fname
from employees
where dtcreated > #dtStartdate
FOR XML PATH (''), TYPE
)
Else if #intReporttype = '3'
SET #xmlResult = (
Select thetime, lname, name, barcode, lcustomerid
from customers
where dtcreated > #dtStartdate
FOR XML PATH (''), TYPE
)
RETURN #xmlResult
END
In SQL it is difficult to create something similar so generic or abstract, especially when it has to do with SELECT of colums. If your purpose is to write as less code as you can in order your sql script to be maintained easily and to be able to add new report types in the future with just minor changes I would suggest to use a stored procedure with dynamic sql. You cannot use a function while you wisk your SELECT to be dynamic, its the wrong method. I would write something like that
CREATE PROCEDURE MyProcedure
(
#ReportType int,
#startdate datetime
)
AS
BEGIN
DECLARE #colNames varchar(MAX),#tblName varchar(MAX),#sSQL varchar(MAX);
SELECT #colNames = CASE
WHEN #ReportType = 1 THEN
'lcustomerid, lname, fname' --You can also add alias
WHEN #ReportType = 2 THEN
'barcode, lname, fname'
WHEN #ReportType = 3 THEN
'thetime, lname, name, barcode, lcustomerid'
ELSE
RAISEERROR('Error msg');
END,
#tblName = CASE
WHEN #ReportType = 1 OR #ReportType = 3 THEN
'customers' --You can also add alias
WHEN #ReportType = 2 THEN
'employees'
ELSE
RAISEERROR('Error msg');
END
SET #sSQL =
'Select '+#colNames+'
from '+#tblName +'
where dtcreated > '''+CONVERT(varchar(10), #startdate, 121)+''''
EXEC(#sSQL)
END
And you will call it as
EXEC MyProcedure 1,'20170131'
for example
With this code every time you want a new report type you will need to add just another line in case with the requested column names. I have used this way in working with Crystal reports and I think it is the best possible solution
If you can use Stored Procedures then for maintainability I would look at using a master stored procedure which calls other stored procedures to return different result sets:
CREATE PROCEDURE MyProc_1(#startdate DateTime)
AS
BEGIN
SELECT lcustomerid, lname, fname
FROM customers WHERE dtcreated > #startdate
END
GO
CREATE PROCEDURE MyProc_2(#startdate DateTime)
AS
BEGIN
SELECT barcode, lname, fname
FROM employees where dtcreated > #startdate
END
GO
CREATE PROCEDURE MyProc_3(#startdate DateTime)
AS
BEGIN
SELECT thetime, lname, name, barcode, lcustomerid
FROM Customers WHERE dtcreated > #startdate
END
GO
CREATE PROCEDURE MyProc(#Reporttype char(1), #startdate DateTime)
AS
BEGIN
IF #Reporttype = '1' EXEC MyProc_1 #startdate
ELSE IF #Reporttype = '2' EXEC MyProc_2 #startdate
ELSE IF #reporttype = '3' EXEC MyProc_3 #startdate
END
GO
And to use:
DECLARE #dt datetime = getdate()
EXEC MyProc 1, #dt
CREATE Proc Emp_det
(
#Reporttype INT,
#startdate DATETIME
)
AS
BEGIN
If #Reporttype = '1' BEGIN
Select lcustomerid, lname, fname
FROM customers
WHERE dtcreated > #startdate
END
ELSE IF #Reporttype = '2' BEGIN
Select barcode, lname, fname
FROM employees
WHERE dtcreated > #startdate
END
ELSE IF #reporttype = '3' BEGIN
Select thetime, lname, name, barcode, lcustomerid
FROM Customers
WHERE dtcreated > #startdate
END
END
GO
Exec Emp_det 1,GETDATE()

SQL insert statement for each update row

Now i made cursor to update in 2 tables and insert in one table based on specific select statement this select statement return 2 columns x , y i need x to update in table "PX" because x is Primary key in this table and need x to update in table "FX" because x is foreign key in this table then insert in third table x data.
I need to change this cursor and use update and insert script i tried but i found that i need to make loop to achieve my target so if any one know if i can change this cursor .
And thanks in advance
DECLARE #id int
DECLARE #clientid uniqueidentifier
DECLARE #code int
DECLARE #Wtime int
DECLARE #closeComplaint CURSOR
SET #closeComplaint = CURSOR FAST_FORWARD
FOR
SELECT ComplaintId, [ClientId]
FROM complaint
WHERE ComplaintStatusId = 5
AND (waitingForCutomerCloseDateTime < GETDATE() OR
waitingForCutomerCloseDateTime = GETDATE())
OPEN #closeComplaint
FETCH NEXT FROM #closeComplaint INTO #id, #clientid
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT
waitingForCutomerCloseTime = #Wtime
FROM
SystemConfiguration
WHERE
ClientId = #clientid
SELECT
[Code] = #code
FROM
[dbo].[resp_users]
WHERE
ClientId = #clientid
UPDATE activity
SET ActivityStatus = 4,
CompletionDate = GETDATE(),
ClosedBy = #code
WHERE [ComplaintId] = #id
UPDATE [dbo].[Complaint]
SET ComplaintStatusId = 2
WHERE [ComplaintId] = #id
INSERT INTO [dbo].[Note] ([Note_Description], [ClientId], [User_Code], [Visible_Internal],
[ComplaintId], [Note_DateTime], [ComplainantId],
[OneStopDesk_CustomerEmail], [OneStopDesk_CustomerUsername], [Private])
VALUES (N'Automatically closed by system after ' + #Wtime, #clientid, #code, 1,
#id, GETDATE(), null, null, null, 1)
FETCH NEXT FROM #closeComplaint INTO #id, #clientid
END
CLOSE #closeComplaint
DEALLOCATE #closeComplaint
I'm not entirely sure I got everything right (you didn't post the table structures, so I can really only guess at times how those tables are connected) - but you should be able to basically do all of this in 3 simple, set-based statements - and that should be a LOT faster than the cursor!
-- declare table variable
DECLARE #Input TABLE (CompaintID INT, ClientID INT)
-- save the rows into a table variable
INSERT INTO #Input (ComplaintID, ClientID)
SELECT ComplaintID, ClientID
FROM dbo.Complaint
WHERE ComplaintStatusId = 5
AND waitingForCustomerCloseDateTime <= GETDATE()
UPDATE a
SET ActivityStatus = 4,
CompletionDate = GETDATE(),
ClosedBy = u.Code
FROM dbo.Activity a
INNER JOIN #Input i ON a.ComplaintId = i.ComplaintId
INNER JOIN dbo.resp_users u ON i.ClientId = u.ClientId
UPDATE dbo.Complaint
SET ComplaintStatusId = 2
WHERE
ComplaintStatusId = 5
AND waitingForCustomerCloseDateTime <= GETDATE()
INSERT INTO dbo.Note ([Note_Description], [ClientId], [User_Code], [Visible_Internal],
[ComplaintId], [Note_DateTime], [ComplainantId],
[OneStopDesk_CustomerEmail], [OneStopDesk_CustomerUsername], [Private])
SELECT
N'Automatically closed by system after ' + sc.waitingForCustomerCloseTime,
i.ClientId, u.Code, 1,
i.ComplaintId, GETDATE(), null, null, null, 1
FROM
#Input i
INNER JOIN
dbo.SystemConfiguration sc ON i.ClientId = sc.ClientId
INNER JOIN
dbo.resp_user u ON u.ClientId = i.ClientId

Loop through a recordset and use the result to do another SQL select and return the results

I am completely new to stored procedure. This time, I need to create a stored procedure in MS SQL.
Let's say I have the following table.
Table name: ListOfProducts
--------------------------
SomeID, ProductID
34, 4
35, 8
35, 11
How do I pass in a SomeID. Use this SomeID to select a recordset from table, ListOfProducts. Then loop through this record set.
Let's say I pass in SomeID = 35.
So, the record set will return 2 records with SomeID 35. In the loop, I will get ProductID 8 and 11, which will be used to do another select from another table.
The stored procedure should return the results from the 2nd select.
How can I do this in MS SQL stored procedure?
Sorry, for this newbie question. Thanks for any help.
If you want looping through the records. You can do like:
--Container to Insert Id which are to be iterated
Declare #temp1 Table
(
tempId int
)
--Container to Insert records in the inner select for final output
Declare #FinalTable Table
(
Id int,
ProductId int
)
Insert into #temp1
Select Distinct SomeId From YourTable
-- Keep track of #temp1 record processing
Declare #Id int
While((Select Count(*) From #temp1)>0)
Begin
Set #Id=(Select Top 1 tempId From #temp1)
Insert Into #FinalTable
Select SomeId,ProductId From ListOfProducts Where Id=#Id
Delete #temp1 Where tempId=#Id
End
Select * From #FinalTable
There is probably no point in writing an explicit loop if you don't need to preform some action on the products that can't be done on the whole set. SQL Server can handle stuff like this much better on its own. I don't know what your tables look like, but you should try something that looks more like this.
CREATE PROC dbo.yourProcName
#SomeID int
AS
BEGIN
SELECT
P.ProductId,
P.ProductName
FROM
Product P
JOIN
ListOfProducts LOP
ON LOP.ProductId = P.ProductId
WHERE
LOP.SomeId = #SomeID
END
I had to do something similar in order to extract hours from a select resultset start/end times and then create a new table iterating each hour.
DECLARE #tCalendar TABLE
(
RequestedFor VARCHAR(50),
MeetingType VARCHAR(50),
RoomName VARCHAR(MAX),
StartTime DATETIME,
EndTime DATETIME
)
INSERT INTO #tCalendar(RequestedFor,MeetingType,RoomName,StartTime,EndTime)
SELECT req as requestedfor
,meet as meetingtype
,room as rooms
,start as starttime
,end as endtime
--,u.datetime2 as endtime
FROM mytable
DECLARE #tCalendarHours TABLE
(
RequestedFor VARCHAR(50),
MeetingType VARCHAR(50),
RoomName VARCHAR(50),
Hour INT
)
DECLARE #StartHour INT,#EndHour INT, #StartTime DATETIME, #EndTime DATETIME
WHILE ((SELECT COUNT(*) FROM #tCalendar) > 0)
BEGIN
SET #StartTime = (SELECT TOP 1 StartTime FROM #tCalendar)
SET #EndTime = (SELECT TOP 1 EndTime FROM #tCalendar)
SET #StartHour = (SELECT TOP 1 DATEPART(HOUR,DATEADD(HOUR,0,StartTime)) FROM #tCalendar)
SET #EndHour = (SELECT TOP 1 DATEPART(HOUR,DATEADD(HOUR,0,EndTime)) FROM #tCalendar)
WHILE #StartHour <= #EndHour
BEGIN
INSERT INTO #tCalendarHours
SELECT RequestedFor,MeetingType,RoomName,#StartHour FROM #tCalendar WHERE StartTime = #StartTime AND EndTime = #EndTime
SET #StartHour = #StartHour + 1
END
DELETE #tCalendar WHERE StartTime = #StartTime AND EndTime = #EndTime
END
Do something like this:
Declare #ID int
SET #ID = 35
SELECT
p.SomeID
,p.ProductID
FROM ListOfProducts p
WHERE p.SomeID = #ID
-----------------------
--Or if you have to join to get it
Declare #ID int
SET #ID = 35
SELECT
c.SomeID
,p.ProductID
,p.ProductName
FROM ListOfProducts p
INNER JOIN categories c on p.ProductID = c.SomeID
WHERE p.SomeID = #ID
You can use option with WHILE loop and BREAK/CONTINUE keywords
CREATE PROC dbo.yourProcName
#SomeID int
AS
BEGIN
IF OBJECT_ID('tempdb.dbo.#resultTable') IS NOT NULL DROP TABLE dbo.#resultTable
CREATE TABLE dbo.#resultTable
(Col1 int, Col2 int)
DECLARE #ProductID int = 0
WHILE(1=1)
BEGIN
SELECT #ProductID = MIN(ProductID)
FROM ListOfProducts
WHERE SomeID = #SomeID AND ProductID > #ProductID
IF #ProductID IS NULL
BREAK
ELSE
INSERT dbo.#resultTable
SELECT Col1, Col2
FROM dbo.yourSearchTable
WHERE ProductID = #ProductID
CONTINUE
END
SELECT *
FROM dbo.#resultTable
END
Demo on SQLFiddle