Show Actual Query Plan causes error? - sql-server

Curious if anyone has experienced this problem before and what the root cause is. The problem is that an error occurs when executed in SQL 2012 with Include Actual Query Plan turned on. It works in 2008 R2 either way (with or without plan) and works in 2012 without the Plan turned on. I discovered this when testing out functionality against partitioned views.
--Setup
USE master
go
DROP DATABASE Test
GO
CREATE DATABASE Test
GO
USE Test
GO
CREATE TABLE DD (pkID int IDENTITY(1,1), FullDate date);
INSERT INTO DD (FullDate) VALUES ('2013-01-01')
INSERT INTO DD (FullDate) VALUES ('2013-01-02')
INSERT INTO DD (FullDate) VALUES ('2013-01-03')
INSERT INTO DD (FullDate) VALUES ('2013-01-04')
INSERT INTO DD (FullDate) VALUES ('2013-01-05')
GO
CREATE TABLE DC (pkID int IDENTITY(1,1), Filter varchar(32), FilterGroup varchar(32));
INSERT INTO DC (Filter, FilterGroup) VALUES ('one', 'groupone')
INSERT INTO DC (Filter, FilterGroup) VALUES ('two', 'grouptwo')
INSERT INTO DC (Filter, FilterGroup) VALUES ('three', 'groupone')
GO
CREATE TABLE FDA1 (pkID int IDENTITY(1,1), fkpID int, fkCID int, fkDateID int)
INSERT INTO FDA1(fkpID, fkCID, fkDateID) VALUES (1,1,1)
INSERT INTO FDA1(fkpID, fkCID, fkDateID) VALUES (1,2,1)
INSERT INTO FDA1(fkpID, fkCID, fkDateID) VALUES (1,1,3)
INSERT INTO FDA1(fkpID, fkCID, fkDateID) VALUES (1,3,5)
GO
CREATE TABLE FDA2 (pkID int IDENTITY(1,1), fkpID int, fkCID int, fkDateID int)
INSERT INTO FDA2(fkpID, fkCID, fkDateID) VALUES (2,1,2)
INSERT INTO FDA2(fkpID, fkCID, fkDateID) VALUES (2,2,2)
INSERT INTO FDA2(fkpID, fkCID, fkDateID) VALUES (2,1,4)
INSERT INTO FDA2(fkpID, fkCID, fkDateID) VALUES (2,3,5)
GO
CREATE VIEW FDA
AS
SELECT pkID, fkpID, fkCID, fkDateID FROM FDA1
UNION ALL
SELECT pkID, fkpID, fkCID, fkDateID FROM FDA2
GO
CREATE FUNCTION GetFilter
(
#pID int
, #filterGroup varchar(32)
)
RETURNS #Filter TABLE
(
CID int
)
AS
BEGIN
INSERT INTO
#Filter
SELECT
dc.pkID
FROM
DC dc
WHERE
dc.FilterGroup = #filterGroup
RETURN
END
GO
CREATE PROC test (#ID int)
AS
BEGIN
BEGIN TRY
DECLARE #FilterGroup varchar(32) = 'groupone'
SELECT
CAST(MIN(dd.FullDate) As datetime) as ProjectReviewStartDate
FROM
dbo.FDA fda
INNER JOIN dbo.DD dd On fda.fkDateID = dd.pkID
INNER JOIN dbo.GetFilter(#ID, #FilterGroup) ctl on fda.fkCID = ctl.CID
WHERE
fda.pkID = #ID
OPTION (RECOMPILE);
RETURN 0;
END TRY
BEGIN CATCH
--Declare variables for error information.
Declare
#ErrorMessage nvarchar(max),
#ErrorSeverity bigint,
#ErrorState int;
--Populate error information.
Select
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
--Re-throw error to calling method.
RAISERROR(#ErrorMessage, #ErrorSeverity, #ErrorState);
--Failure
RETURN -1;
END CATCH;
END
GO
Now that setup is done, let's run the actual code, first "Include Actual Execution Plan"
USE Test
GO
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO
DECLARE #id int = 1
DECLARE #tbl table (dt datetime)
DECLARE #findate datetime
INSERT #tbl EXEC test #id
SELECT #findate = dt FROM #tbl
SELECT #findate
GO
You should receive the result of: 2013-01-01 00:00:00.000.
Now turn on Include Actual Execution Plan and you will receive the result of NULL and see an error (In SQL 2012):
Msg 50000, Level 16, State 10, Procedure test, Line 33
String or binary data would be truncated.
With Include Actual Execution Plan on and removing the Try/Catch block from the proc removes the error and returns the correct result. However again this works fine in 2008R2.
Any ideas?
Thanks

I don't have an answer for you yet, just more ammo that you have likely encountered a bug.
First, to prove that this doesn't involve RAISERROR specifically, changing that line to:
THROW;
Returns this error message instead:
Msg 8152, Level 16, State 10, Procedure test, Line 7
String or binary data would be truncated.
So this shows that the error is happening in the SELECT call and has something to do with the combination of TRY/CATCH and show actual plan.
Next, let's see what the metadata describes as the output for the stored procedure:
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object
(OBJECT_ID('test'), NULL);
Results:
name system_type_name
---------------------- ----------------
ProjectReviewStartDate datetime
Now let's see what it says about the metadata (well, actually, turns out an error message) if we try to test the metadata for the batch you're running:
SELECT [error_message]
FROM sys.dm_exec_describe_first_result_set(N'DECLARE #id int = 1
DECLARE #tbl table (dt datetime)
DECLARE #findate datetime
INSERT #tbl
EXEC test #id', -1, NULL);
The result:
error_message
----------------------------------------------------------
Incorrect syntax near '-'.
The batch could not be analyzed because of compile errors.
There is no - in the batch. And this is with execution plan turned off. And you get the same result even if you comment out the insert in the batch above. Even for example:
SELECT error_message
FROM sys.dm_exec_describe_first_result_set(N'EXEC test 1', -1, NULL);
The error message changes ever so slightly (look closely):
error_message
----------------------------------------------------------
Incorrect syntax near '1'.
The batch could not be analyzed because of compile errors.
We've removed any involvement of showplan or potential truncation and we still can demonstrate that SQL Server has some kind of problem compiling and/or generating metadata for this simple batch.
Seems to be a pretty simple repro, and you have workarounds, but I would definitely file this on Connect.
edit
I see that you have filed a bug; anyone else who can repro this situation should vote and confirm repro:
http://connect.microsoft.com/SQLServer/feedback/details/785151/show-actual-query-plan-causes-error

Related

How to get and use the value returned by a stored procedure to a INSERT INTO... SELECT... statement

I am just new in SQL language and still studying it. I'm having hard time looking for answer on how can I use the stored procedure and insert value into a table.
I have this stored procedure:
CREATE PROCEDURE TestID
AS
SET NOCOUNT ON;
BEGIN
DECLARE #NewID VARCHAR(30),
#GenID INT,
#BrgyCode VARCHAR(5) = '23548'
SET #GenID = (SELECT TOP (1) NextID
FROM dbo.RandomIDs
WHERE IsUsed = 0
ORDER BY RowNumber)
SET #NewID = #BrgyCode + '-' + CAST(#GenID AS VARCHAR (30))
UPDATE dbo.RandomIDs
SET dbo.RandomIDs.IsUsed = 1
WHERE dbo.RandomIDs.NextID = #GenID
SELECT #NewID
END;
and what I'm trying to do is this:
INSERT INTO dbo.Residents([ResidentID], NewResidentID, [ResLogdate],
...
SELECT
[ResidentID],
EXEC TestID ,
[ResLogdate],
....
FROM
source.dbo.Resident;
There is a table dbo.RandomIDs containing random 6 digit non repeating numbers where I'm pulling out the value via the stored procedure and updating the IsUsed column of the table to 1. I'm transferring data from one database to another database and doing some processing on the data while transferring. Part of the processing is generating a new ID with the required format.
But I can't get it to work Sad I've been searching the net for hours now but I'm not getting the information that I need and that the reason for my writing. I hope someone could help me with this.
Thanks,
Darren
your question is little bit confusing, because you have not explained what you want to do. As i got your question, you want to fetch random id from randomids table and after performed some processing on nextid you want to insert it into resident table [newresidentid] and end of the procedure you fetch data from resident table. if i get anything wrong feel free to ask me.
your procedure solution is following.
CREATE PROCEDURE [TestId]
AS
SET NOCOUNT ON;
BEGIN
DECLARE #NEWID NVARCHAR(30)
DECLARE #GENID BIGINT
DECLARE #BRGYCODE VARCHAR(5) = '23548'
DECLARE #COUNT INTEGER
DECLARE #ERR NVARCHAR(20) = 'NO IDS IN RANDOM ID'
SET #COUNT = (SELECT COUNT(NEXTID) FROM RandomIds WHERE [IsUsed] = 0)
SET #GENID = (SELECT TOP(1) [NEXTID] FROM RandomIds WHERE [IsUsed] = 0 ORDER BY [ID] ASC)
--SELECT #GENID AS ID
IF #COUNT = 0
BEGIN
SELECT #ERR AS ERROR
END
ELSE
BEGIN
SET #NEWID = #BRGYCODE + '-' + CAST(#GENID AS varchar(30))
UPDATE RandomIds SET [IsUsed] = 1 WHERE [NextId] = #GENID
INSERT INTO Residents ([NewResidentId] , [ResLogDate] ) VALUES (#NEWID , GETDATE())
SELECT * FROM Residents
END
END
this procedure will fetch data from your randomids table and perform some processing on nextid than after it directs insert it into resident table and if you want to insert some data through user you can use parameter after declaring procedure name
E.G
CREATE PROCEDURE [TESTID]
#PARAM1 DATATYPE,
#PARAM2 DATATYPE
AS
BEGIN
END
I'm not convinced that your requirement is a good one but here is a way to do it.
Bear in mind that concurrent sessions will not be able to read your update until it is committed so you have to kind of "lock" the update so you will get a block until you're going to commit or rollback. This is rubbish for concurrency, but that's a side effect of this requirement.
declare #cap table ( capturedValue int);
declare #GENID int;
update top (1) RandomIds set IsUsed=1
output inserted.NextID into #cap
where IsUsed=0;
set #GENID =(select max( capturedValue) from #cap )
A better way would be to use an IDENTITY or SEQUENCE to solve your problem. This would leave gaps but help concurrency.

insert a field value into a table from c# Form application

I want to Insert a DateTime variable into a table by " Insert into " , I mean by using from coding not wizard.
fro example I have a table which has a some field and one of them is Time_1 and I've write a procedure which its duty is filling Information to my table and in my procedure I've used from :
Insert into my_table
values
('#name','#last_Name',#ID,#Time)
but SQL show error when I enter 12:45:00 for #Time
so please tell me how to enter times into my filed .
Thanks in advance
create procedure Insert_List
#code int, #name nvarchar(20),#lastname nvarchar(40),#time1 datetime,#time2 datetime,
#time3 datetime,#price1 int,#price2 int, #B1 int , #V1 int , #M1 int
as
begin
insert into Employee
values
(#code,#name,#lastname,#time1,#time2,#time3,#price1,#price2,#B1,#V1,#M1)
end
//and I try to execute my procedure by these information
execute Insert_List 1,'Keyvan','Fardi',10:00:00,18:15:00,19:10:00,10,10,10,10
select * from employee
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ':'.
Insert into my_table values ('#name','#last_Name',#ID,#Time)
Since you are using a parameterized SQL statement, do not enclose the parameters in quotes. Instead:
INSERT INTO my_table VALUES (#name, #last_Name, #ID, #Time);
If you are still getting an error, post the error message and all proc code.

Display the number of rows inserted into a table from an existing table

Please how can display the number of rows inserted into an archive table using Microsoft SQL server 2012 with Stored Procedure. I have my code display below. I ran the code successful, but calling the stored procedure, I got error message says
Msg 8114, Level 16, State 5
Procedure p_moveTransHisttoArchive3a, Line 0 Error converting data type varchar to datetime.
Here is my code:
IF OBJECT_ID(N'Production.p_moveTransHisttoArchive3a', N'P') IS NOT NULL
DROP PROCEDURE Production.p_moveTransHisttoArchive3a;
GO
CREATE PROCEDURE Production.p_moveTransHisttoArchive3a
#FiveDaysAgo DATETIME
AS
SET NOCOUNT ON;
BEGIN
DECLARE #counter INT;
SET #counter = ##ROWCOUNT
INSERT INTO Production.TransactionHistoryArchive
SELECT*
FROM Production.TransactionHistory
WHERE TransactionDate <= #FiveDaysAgo
RETURN #counter --No of rows moved
END;
GO
Here is something close to what you want I think. Why is your inbound parameter named FiveDaysAgo? If you want to always get rows that are older than five days ago that belongs INSIDE the proc, not as a parameter.
CREATE PROCEDURE Production.p_moveTransHisttoArchive3a
#FiveDaysAgo DATETIME
, #RowCount int OUTPUT
AS
set nocount on;
INSERT INTO Production.TransactionHistoryArchive
(
Col1
, Col2
)
SELECT Col1
, Col2
FROM Production.TransactionHistory
WHERE TransactionDate <= #FiveDaysAgo
select #RowCount = ##ROWCOUNT

Got error like Only one expression can be specified in the select list when the subquery is not introduced with EXISTS

i am exporting some date to sql server using string builder.and here i assign all the values to particular tables.but i don't know i got some error.i can't resolve.help me to resolve this.
ALTER PROCEDURE [dbo].[usp_CreateEmployeDetails]
#nEmployeeDetailsInXML nvarchar(max)=''
As
DECLARE #iTree INTEGER
declare #empid int
BEGIN
SET NOCOUNT ON;
create table #TempRelation(RowNo int identity(1,1),RelationShipId int,RelativeName nvarchar(100),DOB date,IsNominee bit)
create table #Temp_Present_Address(RowNo int identity(1,1),Street1 nvarchar(200),Street2 nvarchar(200),CountryId int,StateId int,CityId int,AddressTypeId int)
create table #Temp_Permanent_Address(RowNo int identity(1,1),Street1 nvarchar(200),Street2 nvarchar(200),CountryId int,StateId int,CityId int,AddressTypeId int)
create table #TempDetails(RowNo int identity(1,1),EmployeeName nvarchar(100),DOB date,DOJ date,Email nvarchar(100),Phone bigint,BoodGroup nchar(10),
PAN_No nvarchar(15),PF_No nvarchar(100),Sex char(10),AccountNo nvarchar(100),BankName nvarchar(100),BranchId int,ManagerId int,HrId int,DesigId int)
exec sp_xml_preparedocument #iTree output,#nEmployeeDetailsInXML
insert into #TempRelation(RelationShipId,RelativeName,DOB,IsNominee)
select RelationShipId,RelativeName,DOB,IsNominee
from openxml (#iTree,'EmployeeDetails/EmployeeRelation',1)
with(RelationShipId int,RelativeName nvarchar(100),DOB date,IsNominee bit)
insert into #Temp_Permanent_Address(Street1,Street2,CountryId,StateId,CityId,AddressTypeId)
select Street1,Street2,CountryId,StateId,CityId,AddressTypeId
from openxml (#iTree,'EmployeeDetails/EmployeePermanentAdress',1)
with(Street1 nvarchar(200),Street2 nvarchar(200),CountryId int,StateId int,CityId int,AddressTypeId int)
insert into #Temp_Present_Address(Street1,Street2,CountryId,StateId,CityId,AddressTypeId)
select Street1,Street2,CountryId,StateId,CityId,AddressTypeId
from openxml (#iTree,'EmployeeDetails/EmployeePresentAdress',1)
with(Street1 nvarchar(200),Street2 nvarchar(200),CountryId int,StateId int,CityId int,AddressTypeId int)
insert into #TempDetails(EmployeeName,DOB,DOJ,Email,Phone,BoodGroup,PAN_No,PF_No,Sex,AccountNo,BankName,BranchId,ManagerId,HrId,DesigId)
select EmployeeName,DOB,DOJ,Email,Phone,BoodGroup,PAN_No,PF_No,Sex,AccountNo,BankName,BranchId,ManagerId,HrId,DesigId
from openxml (#iTree,'EmployeeDetails/Employee',1)
with(EmployeeName nvarchar(100),DOB date,DOJ date,Email nvarchar(100),Phone bigint,BoodGroup nchar(10),PAN_No nvarchar(15),PF_No nvarchar(100),Sex char(10),
AccountNo nvarchar(100),BankName nvarchar(100),BranchId int,ManagerId int,HrId int,DesigId int)
if((select COUNT(RowNo) from #TempDetails)>0)
begin
insert into Employee(EmployeeName,DOB,DOJ,Email,Phone,BoodGroup,PAN_No,PF_No,Sex)output inserted.EmployeeId select EmployeeName,DOB,DOJ,Email,Phone,BoodGroup,PAN_No,PF_No,Sex from #TempDetails
set #empid=SCOPE_IDENTITY()
if((select COUNT(EmployeeName) from Employee where EmployeeId=#empid)>0)
begin
insert into EmployeeAccountDetls(EmployeeId,AccountNo,BankName)values(#empid,(select AccountNo,BankName from #TempDetails))
insert into EmployeeLink(EmployeeId,BranchId,ManagerId,HrId,DesigId)values(#empid,(select BranchId,ManagerId,HrId,DesigId from #TempDetails))
if((select COUNT(RowNo) from #Temp_Permanent_Address)>0)
begin
insert into EmployeeAddress(EmployeeId,Street1,Street2,CountryId,StateId,CityId,AddressTypeId)values(#empid,(select Street1,Street2,CountryId,StateId,CityId,AddressTypeId from #Temp_Permanent_Address))
end
if((select COUNT(RowNo) from #Temp_Present_Address)>0)
begin
insert into EmployeeAddress(EmployeeId,Street1,Street2,CountryId,StateId,CityId,AddressTypeId)values(#empid,(select Street1,Street2,CountryId,StateId,CityId,AddressTypeId from #Temp_Present_Address))
end
if((select COUNT(RowNo) from #TempRelation)>0)
begin
insert into EmployeeRelationDetls(EmployeeId,RelationShipId,RelativeName,DOB,isNominee)values(#empid,(select RelationShipId,RelativeName,DOB,IsNominee from #TempRelation))
end
end
end
EXEC sp_xml_removedocument #iTree
drop table #Temp_Present_Address
drop table #Temp_Permanent_Address
drop table #TempDetails
drop table #TempRelation
END
why its happening i checked but i didn't get the result
For the specific error you mention, your problem is here:
insert into EmployeeAddress(EmployeeId,Street1,Street2,CountryId,StateId,CityId,AddressTypeId)
values (#empid, (select Street1,Street2,CountryId,StateId,CityId,AddressTypeId from #Temp_Permanent_Address))
You probably want:
insert into EmployeeAddress(EmployeeId,Street1,Street2,CountryId,StateId,CityId,AddressTypeId)
SELECT #empid,Street1,Street2,CountryId,StateId,CityId,AddressTypeId from #Temp_Permanent_Address
However, you're inserting multiple rows and then giving the last rows IDENTITY value to every address row doing that. You need to find a better way of relating keys - which can either be done using MERGE's output clause or by using a stronger key to reference back to the table you just inserted into in order to find the newly inserted employee ID.

result set based on success or failure

I'm having a stored procedure which returns two result sets based on the success or failure.
SP success result set: name, id ,error,desc
SP failure result sret: error,desc
I'm using the following query to get the result of the stored procedure. It returns 0 for success and -1 for failure.
declare #ret int
DECLARE #tmp TABLE (
name char(70),
id int,
error char(2),
desc varchar(30)
)
insert into #tmp
EXEC #ret = sptest '100','King'
select #ret
select * from #tmp
If the SP is success the four field gets inserted into the temp table since the column matches.
But in case of failure the sp result set has only error and desc which does not matchs with no of columns in the temp table...
.I can't change the Sp, so I need to do some thing (not sure) in temp table to handle both failure and success.
You can't return 2 different recordsets and load the same temp table.
Neither can try and fill 2 different tables.
There are 2 options.
Modify your stored proc
All 4 columns are returned in all conditions
1st pair (name, ID) columns are NULL on error
2nd pair (error, desc) are NULL on success
If you are using SQL Server 2005 then use the TRY/CATCH to separate your success and fail code paths. The code below relies on using the new error handling to pass back the error result set via exception/RAISERROR.
Example:
CREATE PROC sptest
AS
DECLARE #errmsg varchar(2000)
BEGIN TRY
do stuff
SELECT col1, col2, col3, col4 FROM table etc
--do more stuff
END TRY
BEGIN CATCH
SELECT #errmsg = ERROR_MESSAGE()
RAISERROR ('Oops! %s', 16, 1, #errmsg)
END CATCH
GO
DECLARE #tmp TABLE ( name CHAR(70), id INT, error char(2), desc varchar(30)
BEGIN TRY
insert into #tmp
EXEC sptest '100','King'
select * from #tmp
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH
My fault!!
Was too quick in the answer.
You need only to relv on the return value, so building up the logic against it is much better.
If you still want to use the temp table, then calling the sptest twice could be a way to deal with it (not optimal though), one time to get the return value and based on it then have 2 different temp tables you are filling up (one would be with the 4 fields, the other only with 2 fields).
declare #ret int
DECLARE #tmp TABLE (name CHAR(70), id INT, error char(2), desc varchar(30))
DECLARE #tmperror TABLE (error char(2), desc varchar(30))
EXEC #ret = sptest '100','King'
IF #ret != 0
BEGIN
INSERT INTO #tmperror
EXEC sptest '100','King';
SELECT * FROM #tmperror;
END
ELSE
BEGIN
INSERT INTO #tmp
EXEC sptest '100','King';
SELECT * FROM #tmp;
END
Keep in mind that this solution is not optimal.
Try modifying your table definition so that the first two columns are nullable:
DECLARE #tmp TABLE (
name char(70) null,
id int null,
error char(2),
desc varchar(30)
)
Hope this helps,
Bill
You cannot do this with just one call. You will have to call it once, either getting the return status and then branching depending on the status to the INSERT..EXEC command that will work for the number of columns that will be returned or Call it once, assuming success, with TRY..CATCH, and then in the Catch call it again assuming that it will fail (which is how it got to the CATCH).
Even better, would be to either re-write the stored procedure so that it returns a consistent column set or to write you own stored procedure, table-valued function or query, by extracting the code from this stored procedure and adapting it to your use. This is the proper answer in SQL.

Resources