How is it possible to use declared parameter, a WITH clause and a cursor in one Query
declare #TOP10 table (Cat Nvarchar(max),SubGUID uniqueidentifier)
declare #Sub uniqueidentifier
declare GUID_Cursor cursor FOR
(select SubGUID from dbo.Sub with(nolock) where year=2016)
;
with [MyTable] as
(
Select SubGUID, color from dbo.Cars with(nolock) where color ='blue'
)
open GUID_Cursor
fetch next from GUID_Cursor into #Sub
while ##FETCH_STATUS=0
begin
insert into #TOP10 (Cat,SubGUID)
select color,SubGUID from [MyTable]
where SubGUID=#Sub
fetch next from GUID_Cursor into #Sub
end
close GUID_Cursor
deallocate GUID_Cursor
select * from #TOP10
I just cant find a way tu open the cursor after using the WITH clause.
Can anyone help me?
Well, aside from the logic of your code (for me - there is no need of cursor here at all), you should just move CTE declaration closer to the place where you're using it.
;with [MyTable] as
(
Select SubGUID, color from dbo.Cars with(nolock) where color ='blue'
)
insert into #TOP10 (Cat,SubGUID)
select color,SubGUID from [MyTable]
where SubGUID=#Sub
But really, all your code could be replaced with:
declare #TOP10 table (Cat Nvarchar(max),SubGUID uniqueidentifier)
insert into #TOP10 (Cat,SubGUID)
select color,SubGUID
from dbo.Cars with(nolock)
where
color ='blue'
and SubGUID in (select SubGUID from dbo.Sub with(nolock) where year=2016)
select * from #TOP10
I agree with #AndyKorneyev, but for the sake of experiment:
declare #crTest CURSOR,
#name VARCHAR(1000),
#type VARCHAR(1000)
set #crTest = CURSOR FAST_FORWARD FOR
WITH cteObjs AS (
SELECT TOP 10 NAME, type
FROM sys.objects o
)
select name, type
from cteObjs
OPEN #crTest
FETCH NEXT FROM #crTest
INTO #name, #type
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #name
PRINT #type
FETCH NEXT FROM #crTest
INTO #name, #type
END
CLOSE #crTest
DEALLOCATE #crTest
Don't want to repeat above snippets but be aware that WITH clause result set must be processed directly at it's end.
Unlike your #tableVar, the scope of the result set that is returned by cte is not valid within complete batch and therefore must be followed by SELECT, INSERT, UPDATE or DELETE statement.
(for details and examples see: https://msdn.microsoft.com/en-us/library/ms175972.aspx or follow one of the other answers).
e. g.
--> declare var:
declare #tableVar table (colOne int, colTwo nvarchar(30)
);
--> use cte:
with preOne as(select carId, color from pcUser with (nolock)
)
--> directly followed by insert:
insert into #tableVar(userId, logInName)
select colOne, colTwo from #tableVar;
--> use var in cursor or anywhere else in the batch
Though I'm not exactly sure what you aim at, perhaps a short join could do the trick here:
select a.[SubGUID]
from [dbo].[Sub] as a with (nolock)
inner join [dbo].[Cars] as b with (nolock) on a.[SubGUID] = b.[SubGUID]
where a.[year] = 2016 and b.[color] = 'blue';
In the end I used temp tabeles like #TOP10. Which I droped after the query. As soon as I got rid of the with [MyTable] as part I had no more problems with the parameters.
Thank you all lot.
Related
I need some help with a stored procedure. It contains a loop which runs a Select query. What I get is 3 tables with result if it loops thrice. How can I combine the results as one table?
The procedure is as below:
CREATE PROCEDURE [dbo].[spGetRndQuestions]
#ExamCode Nvarchar(60)
AS
BEGIN
Declare #NosQues Int, #Catgry nvarchar(50)
DECLARE CategCursor CURSOR FOR
(Select Category From tblExamDetail Where ExamCode = #ExamCode)
OPEN CategCursor
FETCH NEXT FROM CategCursor INTO #Catgry
WHILE ##FETCH_STATUS = 0
BEGIN
SET #NosQues = (Select NoOfQues from tblExamDetail Where ExamCode=#ExamCode AND Category=#Catgry)
SELECT TOP(#NosQues) QM.QuestionID, QM.QuestionDesc, QM.QuestionMarks, QM.Answer1, QM.Answer2, QM.Answer3, QM.Answer4 FROM tblQuestionMaster QM
INNER JOIN tblExamMaster EM ON QM.Dept = EM.Dept AND QM.Location = EM.Location AND QM.QuesModule = EM.ExamModule
Where EM.ExamCode=#ExamCode AND QM.Category =#Catgry
Order by NEWID()
/*SELECT TOP (#NosQues) QuestionID,QuestionDesc,Answer1,Answer2,Answer3,Answer4,QuestionMarks from [dbo].[tblQuestionMaster] Where Category=#Catgry AND
Order by NEWID() */
FETCH NEXT FROM CategCursor INTO #Catgry
END
CLOSE CategCursor
DEALLOCATE CategCursor
END
Thanks, your help is truly appreciated.
Create the table variable in start of your SP of appropriate structure of your return table. In each iteration insert selected data to that table. After deallocating cursor select from that table.
CREATE PROCEDURE [dbo].[spGetRndQuestions]
#ExamCode Nvarchar(60) AS
BEGIN
Declare #NosQues Int, #Catgry nvarchar(50)
DECLARE #tbl TABLE(QuestionID int, QuestionDesc ....)
DECLARE CategCursor CURSOR FOR (Select Category From tblExamDetail Where ExamCode=#ExamCode)
OPEN CategCursor
FETCH NEXT FROM CategCursor INTO #Catgry
WHILE ##FETCH_STATUS = 0
BEGIN
SET #NosQues = (Select NoOfQues from tblExamDetail Where ExamCode=#ExamCode AND Category=#Catgry)
INSERT INTO #tbl
SELECT TOP(#NosQues) QM.QuestionID, QM.QuestionDesc, QM.QuestionMarks, QM.Answer1, QM.Answer2, QM.Answer3, QM.Answer4 FROM tblQuestionMaster QM
INNER JOIN tblExamMaster EM ON QM.Dept = EM.Dept AND QM.Location = EM.Location AND QM.QuesModule = EM.ExamModule
Where EM.ExamCode=#ExamCode AND QM.Category =#Catgry
Order by NEWID()
/*SELECT TOP (#NosQues) QuestionID,QuestionDesc,Answer1,Answer2,Answer3,Answer4,QuestionMarks from [dbo].[tblQuestionMaster] Where Category=#Catgry AND
Order by NEWID() */
FETCH NEXT FROM CategCursor INTO #Catgry
END
CLOSE CategCursor
DEALLOCATE CategCursor
SELECT * FROM #tbl
END
Hamlet does answer the question. However, the query you posted could be optimized to eliminate the need for the cursor or table variable. The following code should do that nicely:
CREATE PROCEDURE [dbo].[spGetRndQuestions]
#ExamCode Nvarchar(60)
AS
SELECT
--A.[category],
B.*
FROM tblExamDetail A
CROSS APPLY (
SELECT TOP (A.[NoOfQues])
QM.QuestionID,QM.QuestionDesc,QM.QuestionMarks,
QM.Answer1,QM.Answer2,QM.Answer3,QM.Answer4
FROM tblQuestionMaster QM
INNER JOIN tblExamMaster EM
ON QM.Dept = EM.Dept
AND QM.Location = EM.Location
AND QM.QuesModule = EM.ExamModule
WHERE EM.ExamCode = A.[ExamCode]
AND QM.Category = A.[Category]
ORDER BY NEWID()
) B
WHERE A.[ExamCode] = #ExamCode
I have declared a temp table to hold all the required values as follows:
DECLARE #temp TABLE
(
Password INT,
IdTran INT,
Kind VARCHAR(16)
)
INSERT INTO #temp
SELECT s.Password, s.IdTran, 'test'
from signal s inner join vefify v
on s.Password = v.Password
and s.IdTran = v.IdTran
and v.type = 'DEV'
where s.[Type] = 'start'
AND NOT EXISTS (SELECT * FROM signal s2
WHERE s.Password = s2.Password
and s.IdTran = s2.IdTran
and s2.[Type] = 'progress' )
INSERT INTO #temp
SELECT s.Password, s.IdTran, 'test'
FROM signal s inner join vefify v
on s.Password = v.Password
and s.IdTran = v.IdTran
and v.type = 'PROD'
where s.[Type] = 'progress'
AND NOT EXISTS (SELECT * FROM signal s2
WHERE s.Password = s2.Password
and s.IdTran = s2.IdTran
and s2.[Type] = 'finish' )
Now i need to loop through the rows in the #temp table and and for each row call a sp that takes all the parameters of #temp table as input.
How can I achieve this?
you could use a cursor:
DECLARE #id int
DECLARE #pass varchar(100)
DECLARE cur CURSOR FOR SELECT Id, Password FROM #temp
OPEN cur
FETCH NEXT FROM cur INTO #id, #pass
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC mysp #id, #pass ... -- call your sp here
FETCH NEXT FROM cur INTO #id, #pass
END
CLOSE cur
DEALLOCATE cur
Try returning the dataset from your stored procedure to your datatable in C# or VB.Net. Then the large amount of data in your datatable can be copied to your destination table using a Bulk Copy. I have used BulkCopy for loading large datatables with thousands of rows, into Sql tables with great success in terms of performance.
You may want to experiment with BulkCopy in your C# or VB.Net code.
something like this?
DECLARE maxval, val, #ind INT;
SELECT MAX(ID) as maxval FROM table;
while (ind <= maxval ) DO
select `value` as val from `table` where `ID`=ind;
CALL fn(val);
SET ind = ind+1;
end while;
You can do something like this
Declare #min int=0, #max int =0 --Initialize variable here which will be use in loop
Declare #Recordid int,#TO nvarchar(30),#Subject nvarchar(250),#Body nvarchar(max) --Initialize variable here which are useful for your
select ROW_NUMBER() OVER(ORDER BY [Recordid] ) AS Rownumber, Recordid, [To], [Subject], [Body], [Flag]
into #temp_Mail_Mstr FROM Mail_Mstr where Flag='1' --select your condition with row number & get into a temp table
set #min = (select MIN(Rownumber) from #temp_Mail_Mstr); --Get minimum row number from temp table
set #max = (select Max(Rownumber) from #temp_Mail_Mstr); --Get maximum row number from temp table
while(#min <= #max)
BEGIN
select #Recordid=Recordid, #To=[To], #Subject=[Subject], #Body=Body from #temp_Mail_Mstr where Rownumber=#min
-- You can use your variables (like #Recordid,#To,#Subject,#Body) here
-- Do your work here
set #min=#min+1 --Increment of current row number
END
You always don't need a cursor for this. You can do it with a while loop. You should avoid cursors whenever possible. While loop is faster than cursors.
I am trying to loop through the data in a table, and using the data to search to return the results in another table.
How do I prevent duplicates from adding to the table? Note that the order of the query results adding are very important. So if the results are already added, I don't want them to be added again. Note that the original ranking done by the full search category is misleading, I don't want to use that.
I am using cursor, but I was told it can be solved using simple query; how do I do that?
Below is the code.
...
DECLARE #subQ NVARCHAR(200)
SET #subQ = ''
DECLARE cur1 CURSOR FOR
SELECT combination FROM #Subqueries
OPEN cur1
FETCH NEXT FROM cur1 INTO #subQ
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #Results (app_id, rank, importance)
SELECT app_id, rank, 1
FROM CONTAINSTABLE(dbo.Applications, display_name, #subQ) KEY_TBL
INNER JOIN Applications App
ON KEY_TBL.[KEY] = App.app_id
FETCH NEXT FROM cur1 INTO #subQ
END
CLOSE cur1
DEALLOCATE cur1
...
You used 1 = 1 in while loop. so it will be showing always true condition and resultant is that loop converted to infinite loop.
Change your condition in while loop.
try this while loop
DECLARE #subQ NVARCHAR(200)
DECLARE #mn int
DECLARE #mx int
DECLARE #val varchar(100)
SET #subQ = ''
;WITH CTE as(select ROW_NUMBER() over (order by (select 0)) as rn,* from #Subqueries)
select #mn=MIN(rn),#mx=MAX(rn) from CTE
WHILE #mn>=#mx
BEGIN
select #val=somecolm from CTE where rn=#mn
--do here for each value of any column in CTE
SET #mn=#mn+1
END
I need to create a temporary table and then update the original table. Creating the temporary table is not a problem.
create table #mod_contact
(
id INT IDENTITY NOT NULL PRIMARY KEY,
SiteID INT,
Contact1 varchar(25)
)
INSERT INTO #mod_contact (SiteID, Contact1)
select r.id, r.Contact from dbo.table1 r where CID = 142
GO
Now I need to loop through the table and update r.contact = SiteID + r.contact
I have never used a while loop before and can't seem to make any examples I have seen work.
You can do this in multiple ways, but I think you're looking for a way using a cursor.
A cursor is sort of a pointer in a table, which when incremented points to the next record. ( it's more or less analogeous to a for-next loop )
to use a cursor you can do the following:
-- DECLARE the cursor
DECLARE CUR CURSOR FAST_FORWARD READ_ONLY FOR SELECT id, siteId, contract FROM #mod_contract
-- DECLARE some variables to store the values in
DECLARE #varId int
DECLARE #varSiteId int
DECLARE #varContract varchar(25)
-- Use the cursor
OPEN CUR
FETCH NEXT FROM CUR INTO #varId, #varSiteId, #varContract
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE dbo.table1
SET contract = #varSiteId + #varContract -- It might not work due to the different types
WHERE id = #varId
FETCH NEXT FROM CUR INTO #varId, #varSiteId, #varContract
END
CLOSE CUR
DEALLOCATE CUR
It's not the most efficient way to get this done, but I think this is what you where looking for.
Hope it helps.
Use a set based approach - no need to loop (from the little details):
UPDATE
r
SET
r.Contact = m.SiteID + r.Contact
FROM
table1 r
INNER JOIN
#mod_contact m
ON m.id=r.id
Your brain wants to do this:
while records
update(i); //update record i
records = records + 1
end while
SQL is set based and allows you to take a whole bunch of records and update them in a single command. The beauty of this is you can use the WHERE clause to filter certain rows that are not needed.
As others have mentioned, learning how to do loops in SQL is generally a bad idea; however, since you're trying to understand how to do something, here's an example:
DECLARE #id int
SELECT #ID =1
WHILE #ID <= (SELECT MAX(ID) FROM table_1)
-- while some condition is true, then do the following
--actions between the BEGIN and END
BEGIN
UPDATE table_1
SET contact = CAST(siteID as varchar(100)) + contact
WHERE table_1.CID = #ID
--increment the step variable so that the condition will eventually be false
SET #ID = #ID + 1
END
--do something else once the condition is satisfied
PRINT 'DONE!! Don't try this in production code...'
Try this one:
-- DECLARE the cursor
DECLARE CUR CURSOR FAST_FORWARD READ_ONLY FOR SELECT column1,column2 FROM table
-- DECLARE some variables to store the values in
DECLARE #varId int
DECLARE #varSiteId int
--DECLARE #varContract varchar(25)
-- Use the cursor
OPEN CUR
FETCH NEXT FROM CUR INTO #varId, #varSiteId
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT *
FROM Table2
WHERE column1 = #varId
AND column2 = #varSiteId
FETCH NEXT FROM CUR INTO #varId, #varSiteId
END
CLOSE CUR
DEALLOCATE CUR
need to create a temporary table and then up date the original table.
Why use a temporary table at all? Your CID column doesn't appear in the temporary table, so I don't see how you can successfully update the original table using SiteID, unless there is only one row where CID = 142 in which using a temp table is definitely overkill.
You can just do this:
UPDATE dbo.table1
SET contact = SiteID + contact
WHERE CID = 142;
Here's a related example which may help getting you to 'think in SQL':
UPDATE T
SET A = B, B = A;
Assuming A and B are of the same type, this would successfully swap their values.
I am not good at SQL Server 2000. I have a comma-delimited list of ids. I need to see if that ID exists in a table. If it does, I want to break out of the loop with that ID saved in a variable that I can use in my stored procedure. This is what I am trying right now:
DECLARE #coreID INT
SET #coreID=NULL
DECLARE #itemID NVARCHAR(50)
DECLARE itemCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT [String] AS 'itemID' FROM dbo.SplitListIntoTable(#myIDs)
OPEN itemCursor
FETCH NEXT FROM itemCursor INTO #itemID
WHILE ##FETCH_STATUS = 0 BEGIN
-- If #itemID EXISTS IN MyTable set #coreID=#itemID and Break. How do I do this?
FETCH NEXT FROM itemCursor INTO #itemID
END
CLOSE itemCursor
DEALLOCATE itemCursor
Thank you!
Ideally, you shouldn't use a cursor as performance won't be great. If you can do it as a set-based statement, do that instead, maybe like this:
SELECT TOP 1 #CoreID = [String]
FROM dbo.SplitListIntoTable(#myIDs) x
JOIN MyTable t ON x.[String] = t.ID
However, if you have a real reason to use a cursor, you can use the BREAK statement to break out of a WHILE loop
e.g.
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS(SELECT * FROM MyTable WHERE Id = #ItemID)
BEGIN
SET #CoreId = #ItemId
BREAK
END
FETCH NEXT FROM itemCursor INTO #itemID
END
I don't know how to do this using a cursor, but I supect you can do this much better (faster) with a a join. If the output of dbo.SplitListIntoTable(#myIDs) is actually an odered table, then you can output a table with another column what is say the string numer, 1, 2, 3, etc...
(I don't have sql in front of me to test this but something like)
create table t(itemNum int identity, itemId nvarchar(max))
insert into t (item id) select 1 from dbo.SplitListIntoTable(#myIDs)
Then join the two and take the top one
set #coreID =
select top 1 #itemID
from MyTable m
inner join t t.itemid = m.itemid
order by m.itemNum asc
of course you could use a CTE, table var or temp table too.