Insert/Update Stored Procedure has conversion error - sql

I am trying to make one stored procedure only in SQL Server that lets the user to choose if he/she wants to add or update a record. Below is my code for my stored pro:
CREATE PROCEDURE Sproc_INSERTUPDATE_tblProducts
#ProductID bigint,
#ProductName varchar(50),
#Description varchar(50),
#Price money,
#DateCreated datetime,
#DateUpdated datetime,
#Choice bit output
AS
BEGIN
Select #Choice
If #Choice = 0
Begin
Insert into tblProducts (
ProductID,
ProductName,
Description,
Price,
DateCreated,
DateUpdated)
values (#ProductID,
#ProductName,
#Description,
#Price,
#DateCreated,
#DateUpdated)
Select * from tblProducts
End
Else If #Choice = 1
Begin
Update tblProducts Set ProductID = #ProductID,
ProductName = #ProductName,
Description = #Description,
Price = #Price,
DateCreated = #DateCreated,
DateUpdated = #DateUpdated
Select * from tblProducts
End
Else
Begin
Print 'Invalid choice. Please choose 0 or 1 only.'
End
END
GO
And here is my code for executing the stored pro I made:
USE StoreDB
Execute Sproc_INSERTUPDATE_tblProducts 4, 'Lotus', 'Flower', 85, GetDate, GetDate, 0
I don't encounter any errors with my stored pro but when I try to execute using a new query, I get this error message:
GetDate(): Error converting data type nvarchar to datetime.

You cannot pass a function such as getdate() into a stored proc. Declare a datetime variable and use that instead.
Declare #now datetime = getdate();
Execute Sproc_INSERTUPDATE_tblProducts 4, 'Lotus', 'Flower', 85, #now, #now, 0

Try this?
Execute Sproc_INSERTUPDATE_tblProducts 4, 'Lotus', 'Flower', 85, GetDate(), GetDate(), 0

Related

Insert into table within function

I would like to insert the #OBV's value into a table inside the function. What is the correct way to achieve it?
alter function CalculateOnBalanceVolume (
#operation varchar(3),
#volume money
)
returns char(4) as
begin
declare #prevOBV as money,
#OBV as money
set #prevOBV = (
select top 1 OnBalanceVolume
from OnBalanceVolume
order by EventTime desc
)
if (#operation = 'add') set #OBV = #prevOBV + #volume
if (#operation = 'sub') set #OBV = #prevOBV - #volume
insert into OBVTable values (#OBV) // error
return #OBV
end;
Functions cannot perform any actions known as side-effecting which includes inserting or updating or deleting from tables, so you cannot use a Function for this.
To use a stored procedure you might have:
create procedure CalculateOnBalanceVolume
#operation varchar(3),
#volume decimal(9,2),
#OBV decimal(9,2) output
as
select top (1) #Obv=OnBalanceVolume +
case when #Operation='add' then #volume else -#volume end
from OnBalanceVolume
order by EventTime desc
insert into OBVTable values (#OBV)
go
And then to invoke the procedure and get your output value you would do for example:
declare #OBV decimal(9,2)
exec CalculateOnBalanceVolume 'add', 100, #OBV output
select #OBV as OutputValue

Code a SQL Server stored procedure to update vc_statusID

I coded a stored procedure called vc_FinishVidCast that accepts an int as an input parameter that will be a vc_VidCastID that we will need to mark as finished. The act of finishing a VidCast means we must change its EndDateTime to be the current Date and Time (think GetDate()) and change the vc_StatusID to the vc_StatusID for the ‘Finished’ status.
alter procedure vc_FinishVidCast
(#vidCastID int, #finished int)
as
begin
update vc_VidCast
set vc_StatusID = #finished
where vc_VidCastID = #vidCastID
end
go
exec vc_FinishVidCast '859', '2'
DECLARE #newVC INT
INSERT INTO vc_VidCast (VidCastTitle, StartDateTime, ScheduleDurationMinutes, vc_UserID,vc_StatusID)
VALUES ('Finally done with sprocs', DATEADD(n, -45, GETDATE()), 45,
(SELECT vc_UserID FROM vc_User WHERE UserName = 'tardy'),
(SELECT vc_StatusID FROM vc_Status WHERE StatusText='Started')
)
SET #newVC = ##identity
SELECT *
FROM vc_VidCast
WHERE vc_VidCastID = #newVC
EXEC vc_FinishVidCast #newVC
SELECT * FROM vc_VidCast WHERE vc_VidCastID = #newVC
I get an error:
Msg 201, Level 16, State 4, Procedure vc_FinishVidCast, Line 179
Procedure or function 'vc_FinishVidCast' expects parameter '#finished', which was not supplied.
You may want to try something like below:
DECLARE #Finished_ID INT
SELECT #Finished_ID = vc_StatusID FROM vc_Status WHERE StatusText='FInished'
EXEC vc_FinishVidCast #newVC,#Finished_ID

Unable to insert into table with Identity Column

I have this procedure:
create proc insertfactors_pf
(
#FactorID int,
#CustomersID int,
#Number int,
#TotalPrice decimal(18, 0),
#PaidPrice decimal(18, 0),
#Date Date,
#ProductID int,
#QTY int
)
AS
BEGIN TRANSACTION
SET IDENTITY_INSERT facetors on
INSERT INTO Factor VALUES (#FactorID, #CustomersID, #Number, #TotalPrice, #PaidPrice,#Date)
SET IDENTITY_INSERT factors off
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
SET IDENTITY_INSERT Product_Factor on
INSERT INTO Produc_Factor values(#FactorID,#ProductID,#QTY)
SET IDENTITY_INSERT Product_Factor off
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
COMMIT
But when I run it I get this error:
Msg 8101, Level 16, State 1, Procedure insertfactors_pf, Line 20 [Batch Start Line 0]
An explicit value for the identity column in table 'Factor' can only be specified when a column list is used and IDENTITY_INSERT is ON.
What am I doing wrong?
The error message seems pretty clear: FactorId is an identity column. You shouldn't set the value for FactorID yourself. Sql Server will set it for you. But if you really want to set it for some insane reason, you need to include a column list in the query like this:
SET IDENTITY_INSERT facetors on
INSERT INTO Factor
(FactorID, CustomerID, Number, TotalPrice, PaidPrice, Date)
VALUES
(#FactorID, #CustomersID, #Number, #TotalPrice, #PaidPrice,#Date)
SET IDENTITY_INSERT factors off
Even better, you should do something more like this, where you don't have to mess with identity insert issues:
create proc insertfactors_pf
(
#CustomersID int,
#Number int,
#TotalPrice decimal(18, 0),
#PaidPrice decimal(18, 0),
#Date Date,
#ProductID int,
#QTY int
)
AS
--Move this to inside the procedure definition. Don't ask for it as an argument
DECLARE #FactorID int
BEGIN TRANSACTION
--Don't mention FactorID anywhere here. Sql Server will take care of it
INSERT INTO Factor
(CustomersID, Number, TotalPrice, PaidPrice, Date)
VALUES
(#CustomersID, #Number, #TotalPrice, #PaidPrice,#Date);
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
--use scope_idenity() to get the FactorID value Sql Server just created
SELECT #FactorID = scope_identity();
INSERT INTO Produc_Factor
(FactorID, ProductID, Qty)
VALUES
(#FactorID,#ProductID,#QTY)
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
COMMIT

Multiple parameters error while creating function in SQL Server

I created a function, now rather passing static value I want to add parameter in the function but after calling function it start throwing an error:
Procedure or function dbo.hello has too many arguments specified.
Function :
Create Function dbo.hello
(#InputstartDate Date, #InputendDate Date)
Returns #attendanceTemp table(STUD_NAME VARCHAR(50),
ATTD_DATE DATE ,
attd_DATEs DATE,
Attendance VARCHAR(20))
As
Begin
Declare #startDate DATE
SET #startDate = #InputstartDate
Declare #endDate Date
SET #endDate = #InputendDate
Declare #dateDifference INT
SET #dateDifference = DATEDIFF(day, #startDate,#endDate) ;
Declare #count INT
SET #count = 0
DECLARE #myTable TABLE (STUD_ID int,
countdd int,
STUD_NAME varchar(50),
AttDate Date
)
While #count <= #dateDifference
Begin
Insert Into #myTable (STUD_ID, countdd, STUD_NAME, AttDate)
Values (1, 123, 'HAIDER', #startDate)
Set #count = #count +1
Set #startDate = DATEADD(day, 1, #startDate)
End
Insert Into #attendanceTemp
Select
tb.STUD_NAME, ATTD_DATE, tb.AttDate,
Case
When att.DETAIL Is Null
Then 'ABSENT'
When att.DETAIL = 'ATTENDACE'
Then 'PRESENT'
End As Attendance
from
#myTable tb
Left Join
ATTENDANCE att on tb.AttDate = att.ATTD_DATE
Where
att.STUD_ID = 1 or att.STUD_ID IS NULL
Return
END
Calling the function:
select *
from dbo.hello('2014-04-01', '2014-04-10');
Error:
Procedure or function dbo.hello has too many arguments specified
Possibly you first created the function with only one parameter.
Then made changes to the 'create function' script, and forgot to deploy?
I would;
1. DROP FUNCTION dbo.hello
2. CREATE FUNCTION dbo.hello, with you script
3. Try executing your function again.
The function seems to work fine (Though I cannot run a full test due to not having table 'ATTENDANCE')

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()