We have a scalar function in our application as below
CREATE function dbo.SCMGetEnvProfileValueFn
(#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS varchar(255)
AS
BEGIN
DECLARE #Value as varchar(255)
SELECT #Value = (SELECT TOP(1) Value FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode
AND Code = #Code)
RETURN ISNULL (#Value, #Default)
END
We converted this function to Table function
CREATE FUNCTION SCMGetEnvProfileValueTblFn
(#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS TABLE
AS
RETURN
(SELECT TOP(1) ISNULL (Value, #Default) AS value
FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode AND Code = #Code)
Below 2 statements shows different output. We do not have a column in the table HVCEnvProfile for this entry. Why the variable #Value is showing NULL when there is no row in the table.
SELECT value
FROM dbo.SCMGetEnvProfileValueTblFn('Registration', 'AdmitDtmEffectsLocationHistory', 'TRUE')
SELECT dbo.SCMGetEnvProfileValueFn('Registration', 'AdmitDtmEffectsLocationHistory', 'TRUE')
If a query returns no rows, then no rows will be returned, wrapping an ISNULL won't change that. Example:
SELECT ISNULL(V.C,0)
FROM (VALUES(1),(2),(3),(4))V(C)
WHERE V.C = 5;
Notice this does not return 0, but nothing.
You need to wrap the entire query in an ISNULL.
CREATE FUNCTION SCMGetEnvProfileValueTblFn (#HierarchyCode varchar(255), -- Usually the subsystem code
#Code varchar(50), -- The Code to find
#Default varchar(255) -- If not found, return this default
)
RETURNS table
AS
RETURN
SELECT ISNULL((SELECT TOP (1) [Value]
FROM HVCEnvProfile
WHERE HierarchyCode = #HierarchyCode
AND Code = #Code
ORDER BY SomeColumn), #Default) AS [Value]; --Don' forgot to change the value of SomeColumn
Don't forget, as well, you need an ORDER BY when using a TOP unless you're "happy" with inconsistent results (which I doubt), so i have added one that you will need to amend.
ALTER PROCEDURE dbo.StoredProcedure8
#emp_code bigint,
#co_id bigint,
#p decimal(8,2) output
AS
SELECT #p = (select sum(tran_value) from emp_ded_ben_trans where emp_code=#emp_code and co_id=#co_id and period_flg=2 and tax_flg=0)
RETURN
To call that sproc and retrieve the output parameter, you do (e.g.):
DECLARE #p DECIMAL(8,2)
EXECUTE dbo.StoredProcedure8 123, 456, #p OUTPUT
-- #p now contains the output value
Update:
You don't need to use RETURN - you are right in that a RETURN can only return an INTEGER. But a return value is different to an OUTPUT parameter which is what you are actually using.
i.e. to get a RETURN value from a sproc, is different syntax:
DECLARE #Result INTEGER
EXECUTE #Result = SomeSproc
Stored procedures aren't made to "return values" - that's what you have stored functions for.
CREATE FUNCTION dbo.CalculateSomething
(#emp_code bigint, #co_id bigint)
RETURNS DECIMAL(8, 2)
AS BEGIN
RETURN
(SELECT SUM(tran_value)
FROM dbo.emp_ded_ben_trans
WHERE
emp_code = #emp_code AND co_id = #co_id
AND period_flg = 2 AND tax_flg = 0)
END
You can then call this stored function like this:
SELECT dbo.CalculateSomething(value_for_emp_code, value_for_co_id)
and get back a DECIMAL(8,2) from the calculation.
Stored procedures will return the number of rows affected by their operation - an INT.
If you need to return a value from a stored proc, you need to use the OUTPUT parameter type and use the technique that AdaTheDev shows - you need to grab the output value into a variable.
First you can use not a Stored Procedure by a Function if you have to return a single value basing on input parameters
If your sp return decimal when you need integer - just cast: SELECT (CAST #d AS INT) but this is very dangerous (possible type overflow)
If your Column tran_value is of Decimal Type, the #p will have decimal values after you run the query...
Create Table #test
(ID1 Int,
ID2 Decimal(8,2)
)
Insert into #test Values (1,1.1)
Insert into #test Values (2,2.2)
Insert into #test Values (3,3.3)
Declare #p Decimal(8,2), #intp int
Select #intp = Sum(ID1), #p = Sum(ID2) from #test
Select #intp as IntegerSum, #p as DecimalSum
Drop Table #test
Output
IntegerSum DecimalSum
----------- ---------------------------------------
6 6.60
Note : You need not have to do anything specific to return the value from Stored Procedure through output parameter... Just assign the value to output parameter inside your SP, it will be returned to the caller automatically.
This means your SP is correct even without the return statement
I currently have the following stored procedure;
CREATE PROCEDURE web.insertNewCampaign
(
#tmp_Id BIGINT,
#tmp_Title VARCHAR(100),
#tmp_Content VARCHAR(8000),
#tmp_Pledge DECIMAL(7,2),
--#tmp_Recipients BIGINT,
#tmp_Date DATETIME,
#tmp_Private BIT,
#tmp_Template BIGINT,
#tmp_AddyBook BIGINT
)
AS
declare #recipients BIGINT
declare #tmp_IDENTITY BIGINT
declare #fave BIGINT
declare #allocation VARCHAR(50)
--insert campaign data
BEGIN TRAN
SELECT #recipients = addMaster_NoRecipients FROM tbl_AddressBookMaster
WHERE addMaster_UserId = #tmp_Id AND addMaster_Key = #tmp_AddyBook;
INSERT INTO TBL_CAMPAIGNS ([campaign_MemberId], [campaign_Title], [campaign_Content], [campaign_Pledge], [campaign_Date], [campaign_Private], [campaign_Template], [campaign_AddressBook], [campaign_Recipients])
VALUES (#tmp_Id, #tmp_Title, #tmp_Content, #tmp_Pledge, #tmp_Date, #tmp_Private, #tmp_Template, #tmp_AddyBook, #recipients)
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
COMMIT
......
So i have 2 questions:
1) How do i divide #tmp_Pledge by #recipients to give #allocation eg:(#allocation = #tmp_Pledge / #recipients)
2) Is it possible to compound these statements into a more efficient statement(s) with #allocation effectively being inserted as a value into the column [campaign_RecipShare], and reducing the need for these declared variables?
Many Thanks for any help you can offer for either question.
;-)
After the first select, you can do this to set #allocation:
set #allocation = #tmp_pledge / #recepients
As for making it more efficient, it's already fairly efficient--you won't go through any less steps, but you can condense the code a bit:
INSERT INTO TBL_CAMPAIGNS (
[campaign_MemberId], [campaign_Title], [campaign_Content],
[campaign_Pledge], [campaign_Date], [campaign_Private],
[campaign_Template], [campaign_AddressBook], [campaign_Recipients],
[capmain_RecipShare])
SELECT
#tmp_Id, #tmp_Title, #tmp_Content,
#tmp_Pledge, #tmp_Date, #tmp_Private,
#tmp_Template, #tmp_AddyBook, addMaster_NoRecipients,
#tmp_Pledge / addMaster_NoReceipients as Allocation
FROM
tbl_AddressBookMaster
WHERE
addMaster_UserId = #tmp_Id
AND addMaster_Key = #tmp_AddyBook
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
This also removes the need for you calculating the #allocation member outside of the insert statement.
1) #tmp_pledge / #recepients - I'll assume allocation is a numeric field of some form in TBL_CAMPAIGNS holding a number in varchar is not a good idea.
2) You just need to build a select that returns all the values from the other table and the parameters matching the columns to insert into.
insert into TBL_CAMPAIGNS ([campaign_MemberId], [campaign_Title], [campaign_Content], [campaign_Pledge], [campaign_Date], [campaign_Private], [campaign_Template], [campaign_AddressBook], [campaign_Recipients], [campaign_allocation)
select #tmp_Id, #tmp_Title, #tmp_Content, #tmp_Pledge, #tmp_Date, #tmp_Private, #tmp_Template, #tmp_AddyBook, addMaster_NoRecipients, #tmp_pledge / addMaster_NoRecipients
FROM FROM tbl_AddressBookMaster
WHERE addMaster_UserId = #tmp_Id AND addMaster_Key = #tmp_AddyBook;
SELECT #tmp_IDENTITY = SCOPE_IDENTITY() --this returns the newly added IDENTITY ID
set #allocation = #tmp_pledge / (#recepients* 1.0)
You want to do that because othewise you will run into integer math and the result will round to an integer.
In the SQL Server, I am trying to insert values from one table to another by using the below query:
delete from tblTable1
insert into tblTable1 select * from tblTable1_Link
I am getting the following error:
Column name or number of supplied values does not match table definition.
I am sure that both the tables have the same structure, same column names and same data types.
They don't have the same structure... I can guarantee they are different
I know you've already created it... There is already an object named ‘tbltable1’ in the database
What you may want is this (which also fixes your other issue):
Drop table tblTable1
select * into tblTable1 from tblTable1_Link
I want to also mention that if you have something like
insert into blah
select * from blah2
and blah and blah2 are identical keep in mind that a computed column will throw this same error...
I just realized that when the above failed and I tried
insert into blah (cola, colb, colc)
select cola, colb, colc from blah2
In my example it was fullname field (computed from first and last, etc)
for inserts it is always better to specify the column names see the following
DECLARE #Table TABLE(
Val1 VARCHAR(MAX)
)
INSERT INTO #Table SELECT '1'
works fine, changing the table def to causes the error
DECLARE #Table TABLE(
Val1 VARCHAR(MAX),
Val2 VARCHAR(MAX)
)
INSERT INTO #Table SELECT '1'
Msg 213, Level 16, State 1, Line 6
Insert Error: Column name or number of
supplied values does not match table
definition.
But changing the above to
DECLARE #Table TABLE(
Val1 VARCHAR(MAX),
Val2 VARCHAR(MAX)
)
INSERT INTO #Table (Val1) SELECT '1'
works. You need to be more specific with the columns specified
supply the structures and we can have a look
The problem is that you are trying to insert data into the database without using columns. SQL server gives you that error message.
Error: insert into users values('1', '2','3') - this works fine as long you only have 3 columns
If you have 4 columns but only want to insert into 3 of them
Correct: insert into users (firstName,lastName,city) values ('Tom', 'Jones', 'Miami')
Beware of triggers. Maybe the issue is with some operation in the trigger for inserted rows.
Dropping the table was not an option for me, since I'm keeping a running log. If every time I needed to insert I had to drop, the table would be meaningless.
My error was because I had a couple columns in the create table statement that were products of other columns, changing these fixed my problem. eg
create table foo (
field1 as int
,field2 as int
,field12 as field1 + field2 )
create table copyOfFoo (
field1 as int
,field2 as int
,field12 as field1 + field2) --this is the problem, should just be 'as int'
insert into copyOfFoo
SELECT * FROM foo
The computed columns make the problem.
Do not use SELECT *. You must specify each fields after SELECT except computed fields
some sources for this issues are as below
1- Identity column ,
2- Calculated Column
3- different structure
so check those 3 , i found my issue was the second one ,
For me the culprit is int value assigned to salary
Insert into Employees(ID,FirstName,LastName,Gender,Salary) values(3,'Canada', 'pa', 'm',15,000)
in salary column When we assign 15,000 the compiler understand 15 and 000.
This correction works fine for me.
Insert into Employees(ID,FirstName,LastName,Gender,Salary) values(4,'US', 'sam', 'm',15000)
Update to SQL server 2016/2017/…
We have some stored procedures in place to import and export databases.
In the sp we use (amongst other things) RESTORE FILELISTONLY FROM DISK where we create a
table "#restoretemp" for the restore from file.
With SQL server 2016, MS has added a field SnapshotURL nvarchar(360) (restore url Azure) what has caused the error message.
After I have enhanced the additional field, the restore has worked again.
Code snipped (see last field):
SET #query = 'RESTORE FILELISTONLY FROM DISK = ' + QUOTENAME(#BackupFile , '''')
CREATE TABLE #restoretemp
(
LogicalName nvarchar(128)
,PhysicalName nvarchar(128)
,[Type] char(1)
,FileGroupName nvarchar(128)
,[Size] numeric(20,0)
,[MaxSize] numeric(20,0)
,FileID bigint
,CreateLSN numeric(25,0)
,DropLSN numeric(25,0) NULL
,UniqueID uniqueidentifier
,ReadOnlyLSN numeric(25,0)
,ReadWriteLSN numeric(25,0)
,BackupSizeInByte bigint
,SourceBlockSize int
,FilegroupID int
,LogGroupGUID uniqueidentifier NULL
,DifferentialBaseLSN numeric(25,0)
,DifferentialbaseGUID uniqueidentifier
,IsReadOnly bit
,IsPresent bit
,TDEThumbprint varbinary(32)
-- Added field 01.10.2018 needed from SQL Server 2016 (Azure URL)
,SnapshotURL nvarchar(360)
)
INSERT #restoretemp EXEC (#query)
SET #errorstat = ##ERROR
if #errorstat <> 0
Begin
if #Rueckgabe = 0 SET #Rueckgabe = 6
End
Print #Rueckgabe
Check your id. Is it Identity? If it is then make sure it is declared as ID not null Identity(1,1)
And before creating your table , Drop table and then create table.
The problem I had that caused this error was that I was trying to insert null values into a NOT NULL column.
I had the same problem, and the way I worked around it is probably not the best but it is working now.
It involves creating a linked server and using dynamic sql - not the best, but if anyone can suggest something better, please comment/answer.
declare #sql nvarchar(max)
DECLARE #DB_SPACE TABLE (
[DatabaseName] NVARCHAR(128) NOT NULL,
[FILEID] [smallint] NOT NULL,
[FILE_SIZE_MB] INT NOT NULL DEFAULT (0),
[SPACE_USED_MB] INT NULL DEFAULT (0),
[FREE_SPACE_MB] INT NULL DEFAULT (0),
[LOGICALNAME] SYSNAME NOT NULL,
[DRIVE] NCHAR(1) NOT NULL,
[FILENAME] NVARCHAR(260) NOT NULL,
[FILE_TYPE] NVARCHAR(260) NOT NULL,
[THE_AUTOGROWTH_IN_KB] INT NOT NULL DEFAULT(0)
,filegroup VARCHAR(128)
,maxsize VARCHAR(25)
PRIMARY KEY CLUSTERED ([DatabaseName] ,[FILEID] )
)
SELECT #SQL ='SELECT [DatabaseName],
[FILEID],
[FILE_SIZE_MB],
[SPACE_USED_MB],
[FREE_SPACE_MB],
[LOGICALNAME],
[DRIVE],
[FILENAME],
[FILE_TYPE],
[THE_AUTOGROWTH_IN_KB]
,filegroup
,maxsize FROM OPENQUERY('+ QUOTENAME('THE_MONITOR') + ','''+ ' EXEC MASTER.DBO.monitoring_database_details ' +''')'
exec sp_executesql #sql
INSERT INTO #DB_SPACE(
[DatabaseName],
[FILEID],
[FILE_SIZE_MB],
[SPACE_USED_MB],
[FREE_SPACE_MB],
[LOGICALNAME],
[DRIVE],
[FILENAME],
[FILE_TYPE],
THE_AUTOGROWTH_IN_KB,
[filegroup],
maxsize
)
EXEC SP_EXECUTESQL #SQL
This is working for me now.
I can guarantee the number of columns and type of columns returned by the stored procedure are the same as in this table, simply because I return the same table from the stored procedure.
In my case, I had:
insert into table1 one
select * from same_schema_as_table1 same_schema
left join...
and I had to change select * to select same_schema.*.
You're missing column name after TableName in insert query:
INSERT INTO TableName**(Col_1,Col_2,Col_3)** VALUES(val_1,val_2,val_3)
In my case the problem was that the SP I was executing returned two result sets, and only the second result set was matching the table definition.