SQL Server stored procedure: how to return column names/values of type failures in variable? - sql

Ambiguous thread name, I apologize. I am not new to SQL, but I'm new to coding longer stored procedures so I don't deal with variables much outside of passing through maybe a table name or returning row count, etc.
I have a stored procedure that is executing an insert from a staging table to a fact table. There are a couple type casts in the insert.
If the insert fails due to a typecast. Is there any way to return the name of the column that failed, along with what the failed value was? How would I code that? I know that Try_parse would make it so the stored procedure doesn't fail on type cast failure, but I want to be able to pass back exactly what column and value failed.
I show an example here:
Create Procedure dbo.Example_Insert
#updateUser varchar(255)
As
Begin
Insert Into dbo.Energy_Costs (Energy_Cost_Id, Project_Id, Propane_Cost_Dollars,
Electricity_Cost_Dollars, Fuel_Savings_Evaluator)
Select
Next Value For energy_cost_id,
r.project_id,
Cast(r.propane_cost_dollars As Decimal(18,2)),
Cast(r.electricity_cost_dollars As Decimal(18,2)),
#update_user fuel_savings_evaluator
From
staging_table r
return ##ROWCOUNT
end

You can use CURSOR in sql then insert one line at a time. When insert fail return value currently row error.
I hope my idea suitable with you.

Related

Get a specific column from a returned result of SP?

I have a stored procedure that returns a result, let's say it return rows of products. But each product status is not in our hand(can't get it). Our DBA just gave us another stored procedure to get the status of a product. We need to get individual product status by calling their SP. Let's say we have Product table,
CREATE TABLE PRODUCTS
(
ID INT,
Name NVARCHAR(100)
)
CREATE PROCEDURE GetProducts
AS
BEGIN
INSERT INTO #TEMPTABLE
SELECT * FROM PRODUCTS; -- Yes Too Much simplified
-- Create cursor and set additional status in #TEMPTABLE
END;
EXEC GetStatus #ProductId; -- SP That need to get status
The problem is that GetStatus is only way to get the status and this sp sometimes return 2 columns, sometimes 4 and sometimes 8. The return columns will always include Status column for sure.
If columns names is fixed then there is no problem. Is there is a way to create dynamic table at the time of executing SP.
Tried this but not working,
WITH DynamicTable AS
(
EXEC GetStatus
)
The answer to your question is no. There is no good way to get the value of a specific column returned by a stored procedure that can return a dynamic set of columns.
I say no "good" way, because of course there's a WAY. You can write an INSERT EXEC statement for every possible set of columns that the procedure can return and wrap each one in a TRY..CATCH block. If the first one errors, try the next one. As soon as you hit one that doesn't error, get the Status from it, and skip the rest.
That's the answer to your question. The solution to your problem, however, is to replace the GetStatus stored procedure with a Table-valued function. Then you can select from it, join to it, etc. I think the function would have to always return a consistent number of columns, but that would be better anyway, and the columns that aren't needed in a specific case could just be left empty or NULL.

SELECT Query selecting values based on a value in another table

I have 2 tables
Account(AccountId, Encoding)
DeviceAccountMap(AccountId, DeviceId)
Now I need to fetch the devices from the DeviceAccountMap. I pass a list of AccountId to a stored procedure and while fetching the DeviceId from the DeviceAccountMap table I need to compare the Encoding value for each account with a particular value.
Which is the easy way to do this? I am totally lost.
The select clause in the stored procedure will look something like this:
DECLARE #Accounts [usp].[Array]
and [usp].[Array] is defined as below
CREATE TYPE [usp].[Array] AS TABLE
(
Value VARCHAR(36) NULL
)
SELECT
DeviceId,
AccountEncoding = A.Encoding
FROM
usp.DeviceControllerAccountMap DCAM
INNER JOIN
usp.Account A ON (DCAM.AccountId = A.AccountId)
WHERE
DCAM.AccountId IN (SELECT Value From #AccountIds)
AND DCAM.IsShared = 1
AND AccountEncoding LIKE A.Encoding + '.%'
In other words I need to fetch the encoding value for each account and use that in this where clause.
So you can look up information on Table-Valued Parameters (TVPs) in T-SQL.
Here is an article by Erland Sommarskog.
You can refer to this StackOverflow answer to see an example of C# code calling a stored procedure that uses a TVP. I believe TVPs require SQL Server 2008 or higher.
TVPs, as far as I understand, provide a way to make your own data type in sql server that gets treated as if it was a table. You're doing this when you declare your Array type and then when you use the #AccountIds in your stored procedure's select statement.
CREATE TYPE [usp].[Array] AS TABLE -- maybe choose a more descriptive name than 'Array'
(
Value VARCHAR(36) NULL -- choose a more descriptive name than 'Value'
)
CREATE PROCEDURE [usp].[your_procedure_name]
#AccountIds [usp].[Array] READONLY -- use TVP as a parameter
AS
SELECT …
It is not clear form your question details whether you also mean to have a parameter in the stored procedure for the Encoding. It seems like you're looking for accounts whose Encodings start with a period '.'.
So first, create your type, like you're doing.
Then create your stored procedure.
Then test your stored procedure, something like this:
DECLARE #mylist Array -- make TVP sample data
INSERT #mylist(Value) VALUES(1),(11),(27),(123) -- insert some values
exec your_procedure_name #mylist -- run stored procedure
The following line is completely unnecessary. The JOIN to Account does this filter for you.
DCAM.AccountId IN (SELECT Value From #AccountIds)
Or am I missing something?

Insert into table stored procedure results plus variable

I need to insert into a table the results of a stored procedure(SP) plus a couple of other variables.
I know how to insert the SP results but not the variables as well. Is there a way I can do this without having to write a separate update query or pass/return the variable into the SP.
I.e.
INSERT INTO contacttable(name, address, telnum)
EXEC GetContactDetails #ContactId
UPDATE contacttable SET linkId = #LinkId where id = #ContactId
Can I pass the #linkId variable into the INSERT in anyway rather than having to do the separate update?
Thanks.
You can't do this the way you explain your current scenario is.
You either modify the proc to receive the extra parameter and you return it from there so that the insert statements already has this parameter, or you continue doing what you are doing.
Another possibility would be to change that proc into a table-valued function in a way that you can specifically select the columns you need from the resultset and you add the extra parameter in the insert. Something like:
INSERT INTO contacttable(name, address, telnum,linkid)
select name, address,telnum,#linkid from fnGetContactDetails(#ContactID)

Sql select insert debugging

I would like to find a quick method for debugging a insert-select statement.
Example:
Create table tbl_int (
tabid int identity,
col1 bigint)
Create table tbl_char(
tabid int identity,
col2 nvarchar(255))
insert into tbl_char(col2)
select '1' union
select '2' union
select 'a'
insert into tbl_int(col1)
select col2
from tbl_char
Of course, the insert select above fails to run and it is obvious that 'a' cannot be converted to bigint. But what happens when I have 1 milion records in tbl_char. Is there any way of finding the source value of the error:
"Error converting data type nvarchar to bigint."
P.s. Using a convert or cast function and scanning the table with top until finding the right value is a little bit too expensive.
Why don't you wrap the SQL that throw the exception into a Try/Catch block to have more info about it
BEGIN TRY
SELECT *
FROM sys.messages
WHERE message_id = 21;
END TRY
GO
-- The previous GO breaks the script into two batches,
-- generating syntax errors. The script runs if this GO
-- is removed.
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber;
END CATCH;
GO
the below are all the error information you can check
ERROR_NUMBER() returns the error number.
ERROR_MESSAGE() returns the complete text of the error message. The text includes the values supplied for any substitutable parameters such as lengths, object names, or times.
ERROR_SEVERITY() returns the error severity.
ERROR_STATE() returns the error state number.
ERROR_LINE() returns the line number inside the routine that caused the error.
ERROR_PROCEDURE() returns the name of the stored procedure or trigger where the error occurred.
http://msdn.microsoft.com/en-us/library/ms179296.aspx
if this is not enough you can even write a query to select all the records where certain field is not convertible to a number(in your case there is a NVarChar which is not convertible)
The following example uses ISNUMERIC to return all the postal codes that are not numeric values.
SELECT City, PostalCode
FROM Person.Address
WHERE ISNUMERIC(PostalCode)<> 1
http://msdn.microsoft.com/en-us/library/ms186272.aspx
Let's start with why are you trying to send data that is not the same data type to another table? If you do not want non integer data in the column why are you allowing it to be string data to begin with? So first you need to look at if you have a design issue that is affecting data integrity.
If you are doing imports from some other system and have no control over the data type the use, then you need to clean the data before doing the insert. There is no one size fits all fix for this. You will have to write code to find all data that won't meet the terms of the table you are moving it to datatype by data type and field by field. This may be much more complex than using isnumeric (which can have false positives especially if there is decimal information in there as well.) and isdate() and you may need to write functions specific to your needs. It can take a long time to get the cleaning correct. You might need to restrict the values to a certain subset or have a conversion table that converts what they put inthe table to what your system will accept. Suggest you identify the bad data first, move it to an exception table and then do the insert based on records not in the exception table.
As you understand, the char value converted into bigint when you execute insert..select.
So, we cannot avoid the converting, but we can try to avoid the second table scanning.
I propose to create INSTEAD OF INSERT trigger for table tbl_int.
In the body of this trigger you can convert the char value into bigint, and if you recieve the error, you can insert this char value into staging table. If there is no error when convering, you can insert this bigint value into your tbl_int table.

checking whether stored procedure inserted record

i have written stored procedure, where i am first checking whether the record for a particular is repeated more than thrice, if yes it should not insert new record, else it should insert the record in the database.
now in the if condition i have insert query, now i want to know how should i be able to know whether the insert query fired successfully or not. because if the IF statement fails it wont execute insert query, and what should i write in the else statement, so that i can come to know whether the insert query executed or not.
i am using VB.Net as a front end... please tell me the condition how i will be able to know the insert query fired or not.
Regards
Abbas Elecrticwala
you can return a bit value depending on the insert. like
create proc myproc
-- your variables here
as
begin
if (your condition )
begin
your insert query
select '1'
end
else
begin
select '0'
end
end
you can check the result in vb, and you will know depending on the bit value.