Capturing ##error and ##rowcount from a single query - tsql

This seems like it should be pretty simple, but I cannot find a way to do it.
set nocount on;
Update MyTable
set MyField = 'value'
--where 1 = 1/0 -- comment in to test getting an error.
print convert(varchar, ##error)
print "blah blah" + convert(nvarchar, ##rowcount) -- this is always zero because of the previous statement
I tried storing them in a variable, but setting the variable generates a new ##rowcount and new ##error value.
I also tried using an if condition, because i don't care about the row count. But evaluating the if seems to also reset ##rowcount.

You can do it in a single statement, like this:
DECLARE #err INT, #cnt INT
SELECT #err=##ERROR, #cnt=##ROWCOUNT

Related

TSQL break loop when ##ROWCOUNT = 0

I have insert statements (simplified) in a SPROC like the following
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
-- EDIT: Realized forgot to include this following vital line that is causing issue
SET #var = #var + ##ROWCOUNT
-- ##ROWCOUNT now takes on a value of 1, which will cause the following IF check to fail even when no lines are inserted
IF(##ROWCOUNT = 0)
BEGIN
BREAK
END
END
But the issue is, after any operation even when no more rows fit my some_condition, ##ROWCOUNT is equal to 1, not 0.
How can I break that loop when there are 0 rows returned matching my some_condition?
The "set" statement creates a row count of 1. What you should do is immediately save ##ROWCOUNT into a #rowCount variable and use that var later on.
declare #rowCount int
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
-- EDIT: Realized forgot to include this following vital line that is causing issue
SET #rowCount = ##ROWCOUNT
SET #var = #var + #rowCount
-- ##ROWCOUNT now takes on a value of 1, which will cause the following IF check to fail even when no lines are inserted
IF(#rowCount = 0)
BEGIN
BREAK
END
END
Also, you can simplify by setting #rowCount to -1 initially and changing the WHILE condition to #rowCount <> 0. The conditional BREAK will no longer be needed.
An alternative solution. This checks each iteration to see if the ID of the last inserted record has changed or not. If it hasn't changed, it indicates that no records were added that iteration.
SET ROWCOUNT 100
declare #id int;
WHILE(1=1)
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
IF(#id= ##identity)
BEGIN
BREAK
END
set #id = ##identity;
END
Try this solutions:
1st solution
Using ##ROWCOUNT in loop's condition.
SET ROWCOUNT 100
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
WHILE(##ROWCOUNT > 0)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
END
2nd solition
Using goto.
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
IF(##ROWCOUNT = 0)
BEGIN
goto label
END
END
label1:
print 'After lopp'
I think you should use select to get the ##rowcount into a variable. try this:
declare #number_of_rows int
SET ROWCOUNT 100
WHILE(1=1)
BEGIN
INSERT INTO table1
SELECT *
FROM table2
WHERE some_condition
SELECT #number_of_rows=##ROWCOUNT
IF (#number_of_rows = 0)
BEGIN
BREAK
END
END
Implemented solution similar to Moho, but used SELECT instead of SET to store ##ROWCOUNT.

TSQL: Determine number of columns returned by Stored Procedure

This probably has been asked before, but I was unable to find a satisfying answer.
I need to insert results of a stored procedure into a temporary table, something like:
INSERT INTO #TEMP EXEC MY_SP
I don't know in advance how many columns the SP will return, so I need to prepare my #TEMP table (via dynamic ALTER .. ADD commands) to add columns to match SP resultset.
Assumption is - SP accepts no parameters and number of columns is always the same. But how do I determine that number in pure TSQL outside of SP so I can store it for example into a variable?
Tough one, especially if someone else is denying you the necessary permissions to run e.g. OPENROWSET.
Have you considered unpacking/script the SP and add its contents directly to your T-SQL? In this way you can modify and adapt it however you may want.
Otherwise, if you could explain more about the SP:
What does the SP do?
What kind of information does it output? One-N columns, - how many rows?
Is it slow/fast? (Could we perhaps use a brute-force [try-catch] approach)?
How does it determine the columns to output and how often does that change?
Can you pre-determine the columns in any way? (So that you may use an INSERT #temp EXEC sp_getData syntax).
Best of luck!
It's a bit awkward, but you can do something like:
SELECT * INTO #temp
FROM OPENROWSET('SQLOLEDB','Data Source=MyServer;Trusted_Connection=yes;Integrated Security=SSPI', 'EXECUTE MyDB.MySchema.MyProcedure #MyParm=123')
I've requested an EXECUTE INTO syntax, like SELECT INTO to avoid having to know the shape of the stored proc output in advance, but it was rejected
Let me say from the start that if I had to do this I would try find a way to do it outside the SQL environment or something else, because the solution I am providing is not a good way to do this, but it works. So I am not saying that this is a good idea.
I have a sp called test:
CREATE PROCEDURE Test
AS
SELECT 1 as One, 2 as Two
To execute this dynamically I do the following:
DECLARE #i int
SET #i = 1;
DECLARE #SUCESS bit
SET #SUCESS = 0
WHILE(#SUCESS = 0)
BEGIN
DECLARE #proc VARCHAR(MAX)
DECLARE #count int
SET #count = 1
SET #proc = 'DECLARE #t TABLE ( c1 varchar(max) '
WHILE #count < #i
BEGIN
SET #proc = #proc + ', c' + CONVERT(varchar, #count + 1) + ' varchar(max) '
print #proc
SET #count = #count + 1
END
SET #proc = #proc + '); INSERT INTO #t EXEC Test'
BEGIN TRY
EXEC(#proc);
SET #SUCESS = 1
END TRY
BEGIN CATCH
SET #i = #i+1
END CATCH
END
SET #proc = #proc + '; SELECT * into ##t FROM #t '
EXEC( #proc )
SELECT * from ##t
This is a poor solution to your problem because you have lost the data type of your columns, their names etc.
I don't understand the syntax, and this probably isn't the best way, but someone seems to have done this with converting to xml and parsing it: Dynamic query results into a temp table or table variable

Executing if ##rowcount>0 resets ##rowcount to 0. Why?

Link
##Rowcount is used to inform the number of rows affected for the last
select,insert,update or delete statements
declare #row int select 100 if ##rowcount>0 set #row=##rowcount ...
The above will return 0 because as soon as if ##rowcount>0 is executed
it is reset to 0 as it doesn't return any rows. So always assign to
variable first
Why does statement if ##rowcount>0 reset ##rowcount to 0? Isn't the value of ##rowcount affected only by select,insert,update and delete statements?
thank you
It is affected by the last statement. Like this SET statement
Declare #row int
select 100 union all select 200 union all select 300
set #row = ##rowcount;
SELECT #row, ##rowcount
If you read the actual Microsoft SQL Server Docs on MSDN, it gives examples of what statements affect ##ROWCOUNT. For example "such as" implies other statements like IF will also set it to zero
Statements such as USE, SET <option>, DEALLOCATE CURSOR, CLOSE CURSOR, BEGIN TRANSACTION or COMMIT TRANSACTION reset the ROWCOUNT value to 0.
Well,
I faced similar issue using sybase, which indicates that something might be wrong with "if".
declare #counter1 int
declare #counter2 int
select #counter1 = ##rowcount
if ##rowcount = 0 return
select #counter2 = ##rowcount
the output is:
counter1 = 3
counter2 = 0
I would expect 3(original one) or 1(because of Select).
This is even more strange because of
http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc38151.1540/doc/html/san1278452893271.html
"The ##rowcount is not reset to zero by any statement which does not affect rows, such as an IF statement."
on other hand there is a lot of confusion how it really works(based on discussion in internet)
At the end in my solution in first line I assign ##rowcount to variable and my logic is based on that variable

How to handle an empty result set from an OpenQuery call to linked analysis server in dynamic SQL?

I have a number of stored procedures structured similarly to this:
DECLARE #sql NVARCHAR(MAX)
DECLARE #mdx NVARCHAR(MAX)
CREATE table #result
(
[col1] NVARCHAR(50),
[col2] INT,
[col3] INT
)
SET #mdx = '{some dynamic MDX}'
SET #sql = 'SELECT a.* FROM OpenQuery(LinkedAnalysisServer, ''' + #mdx + ''') AS a'
INSERT INTO #result
EXEC sp_executesql #sql
SELECT * FROM #result
This works quite well when results exist in the cube. However, when the OpenQuery results are empty, the INSERT fails with this error:
Column name or number of supplied
values does not match table
definition.
My question is, what is the best way to handle this scenario? I'm using the results in a static report file (.rdlc), so the explicit typing of the temp table is (I'm pretty sure) required.
Use TRY/CATCH in your stored procedure, you'll notice there is a specific error number for your problem, so check the error number and if it is that, return an empty result set. As you already have the table defined that'll be easier.
PseudoCode looks something like this:
SET #mdx = '{some dynamic MDX}'
SET #sql = 'SELECT a.* FROM OpenQuery(LinkedAnalysisServer, ''' + #mdx + ''') AS a'
BEGIN TRY
INSERT INTO #result
EXEC sp_executesql #sql
END TRY
BEGIN CATCH
IF ERROR_NUMBER <> 'The error number you are seeing'
BEGIN
RAISERROR('Something happened that was not an empty result set')
END
END CATCH
SELECT * FROM #result
You'll want to check for that particular error, so that you don't just return empty result sets if your SSAS server crashes for example.
There is another solution to this issue, similar to the accepted answer, which involves using an IF statement instead of TRY...CATCH.
http://www.triballabs.net/2011/11/overcoming-openquery-mdx-challenges/
IF (SELECT COUNT(*)
FROM OPENQUERY("SSAS1",
'SELECT [Measures].[Target Places] ON COLUMNS
FROM [ebs4BI_FactEnrolment]
WHERE [DimFundingYear].[Funding Year].&[17]')) > 0
EXEC sp_executesql N'SELECT CONVERT(varchar(20),
"[DimPAPSCourse].[Prog Area].[Prog Area].[MEMBER_CAPTION]")
as ProgArea,
convert(float, "[Measures].[Target Places]") as Target
FROM OPENQUERY("SSAS1",
''SELECT [Measures].[Target Places] ON COLUMNS,
[DimPAPSCourse].[Prog Area].[Prog Area] ON ROWS
FROM [ebs4BI_FactEnrolment]
WHERE [DimFundingYear].[Funding Year].&[17]'')'
ELSE
SELECT '' as ProgArea, 0 as Target
WHERE 1=0

Array-like access to variables in T-SQL

In my stored procedure I have multiple similar variables #V1, #V2 ... #V20 (let's say 20 of them) FETCHED from a record. How would I use dynamic SQL to make 20 calls to another stored procedure using those variables as parameters?
Of course #V[i] syntax is incorrect, but it expresses the intent
fetch next from maincursor into #status, #V1, #V2, ...
while #i<21
begin
-- ??? execute sp_executesql 'SecondSP', '#myParam int', #myParam=#V[i]
-- or
-- ??? execute SecondSP #V[i]
set #i = #i+1
end
As others have said, set up a temporary table, insert the values that you need into it. Then "iterate" through it executing the necessary SQL from those values. This will allow you to have 0 to MANY values to be executed, so you don't have to set up a variable for each.
The following is a complete sample of how you may go about doing that without cursors.
SET NOCOUNT ON
DECLARE #dict TABLE (
id INT IDENTITY(1,1), -- a unique identity column for reference later
value VARCHAR(50), -- your parameter value to be passed into the procedure
executed BIT -- BIT to mark a record as being executed later
)
-- INSERT YOUR VALUES INTO #dict HERE
-- Set executed to 0 (so that the execution process will pick it up later)
-- This may be a SELECT statement into another table in your database to load the values into #dict
INSERT #dict
SELECT 'V1Value', 0 UNION ALL
SELECT 'V2Value', 0
DECLARE #currentid INT
DECLARE #currentvalue VARCHAR(50)
WHILE EXISTS(SELECT * FROM #dict WHERE executed = 0)
BEGIN
-- Get the next record to execute
SELECT
TOP 1 #currentid = id
FROM #dict
WHERE executed = 0
-- Get the parameter value
SELECT #currentvalue = value
FROM #dict
WHERE id = #currentid
-- EXECUTE THE SQL HERE
--sp_executesql 'SecondSP', '#myParam int', #myParam =
PRINT 'SecondSP ' + '#myParam int ' + '#myParam = ' + #currentvalue
-- Mark record as having been executed
UPDATE d
SET executed = 1
FROM #dict d
WHERE id = #currentid
END
Use a #TempTable
if you are at SQL Server 2005 you can create a #TempTable in the parent stored procedure, and it is available in the child stored procedure that it calls.
CREATE TABLE #TempTable
(col1 datatype
,col2 datatype
,col3 datatype
)
INSERT INTO #TempTable
(col1, col2, col3)
SELECT
col1, col2, col3
FROM ...
EXEC #ReturnCode=YourOtherProcedure
within the other procedure, you have access to #TempTable to select, delete, etc...
make that child procedure work on a set of data not on one element at a time
remember, in SQL, loops suck performance away!
Why not just use the table variable instead, and then just loop through the table getting each value.
Basically treat each row in a table as your array cell, with a table that has one column.
Just a thought. :)
This seems like an odd request - will you always have a fixed set of variables? What if the number changes from 20 to 21, and so on, are you constantly going to have to be declaring new variables?
Is it possible, instead of retrieving the values into separate variables, to return them each as individual rows and just loop through them in a cursor?
If not, and you have to use the individual variables as explained, here's one solution:
declare #V1 nvarchar(100)
set #V1 = 'hi'
declare #V2 nvarchar(100)
set #V2 = 'bye'
declare #V3 nvarchar(100)
set #V3 = 'test3'
declare #V4 nvarchar(100)
set #V4 = 'test4'
declare #V5 nvarchar(100)
set #V5 = 'end'
declare aCursor cursor for
select #V1
union select #V2 union select #V3
union select #V4 union select #V5
open aCursor
declare #V nvarchar(100)
fetch next from aCursor into #V
while ##FETCH_STATUS = 0
begin
exec TestParam #V
fetch next from aCursor into #V
end
close aCursor
deallocate aCursor
I don't really like this solution, it seems messy and unscalable. Also, as a side note - the way you phrased your question seems to be asking if there are arrays in T-SQL. By default there aren't, although a quick search on google can point you in the direction of workarounds for this if you absolutely need them.