sql: my data repeats itself - sql

When I execute my code it works but my data repeats itself. The print is just to see what it gets.
DECLARE #Variable1 NVARCHAR(MAX)
DECLARE #Variable2 NVARCHAR(MAX)
DECLARE #Variable3 NVARCHAR(MAX)
CREATE TABLE #Temp1 (MAI_ID BIGINT, FUN_ID BIGINT)
CREATE TABLE #tmp2 (MAI_ID BIGINT, Variable1 NVARCHAR(MAX),Variable2 NVARCHAR(MAX), Variable3 NVARCHAR(MAX))
INSERT INTO #Temp1
SELECT TOP 10 ISD_MainID, ISNULL(ISD_FUNID,0)
FROM [dev_SAB_EM].[dbo].[SiteDetails]
ORDER BY ISD_ID DESC
DECLARE #MAI_ID BIGINT
DECLARE #FUN_ID BIGINT
WHILE (SELECT COUNT(MAI_ID) FROM #Temp1) <> 0
BEGIN
SELECT TOP 1 #MAI_ID = MAI_ID, #FUN_ID = FUN_ID FROM #Temp1
PRINT #MAI_ID
PRINT #FUN_ID
SELECT #Variable1 = ISNULL(FUN_Name,'') FROM [dev_SAB_Man].[dbo].[fx_GetFUNStructureCTE_Asc] (#FUN_ID) WHERE FUN_Level = 1
SELECT #Variable2 = ISNULL(FUN_Name,'') FROM [dev_SAB_Man].[dbo].[fx_GetFUNStructureCTE_Asc] (#FUN_ID) WHERE FUN_Level = 2
SELECT #Variable3 = ISNULL(FUN_Name,'') FROM [dev_SAB_Man].[dbo].[fx_GetFUNStructureCTE_Asc] (#FUN_ID) WHERE FUN_Level = 3
INSERT INTO #tmp2(MAI_ID, Variable1, Variable2, Variable3)
SELECT #MAI_ID, #Variable1, #Variable2, #Variable3
DELETE FROM #Temp1 WHERE MAI_ID = #MAI_ID AND FUN_ID = #FUN_ID
END
SELECT * FROM #tmp2
DROP TABLE #Temp1
DROP TABLE #tmp2
fx_GetFUNStructureCTE_Asc
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[fx_GetFUNStructureCTE_Asc] (#param_FUNID int)
RETURNS #FUN_Names table
(
[Level_Label] nvarchar(255),
[FUN_Name] nvarchar(255),
[FUN_Level] int,
[FUN_ID] int
)
AS
BEGIN
with cte([Level_Label],[FUN_Name],[FUN_Level],[FUN_ID],[FUN_FUNID]) as
(
select ful1.FUL_Name,fu1.FUN_Name,fu1.FUN_Level,fu1.FUN_ID,fu1.FUN_FUNID
from FunctionalUnits fu1
inner join FunctionalUnitLevels ful1 on ful1.FUL_Level=fu1.FUN_Level
where fu1.FUN_ID=#param_FUNID
union all
select ful2.FUL_Name,fu2.FUN_Name,fu2.FUN_Level,fu2.FUN_ID,fu2.FUN_FUNID
from FunctionalUnits fu2
inner join FunctionalUnitLevels ful2 on ful2.FUL_Level=fu2.FUN_Level
inner join CTE a on a.FUN_FUNID=fu2.FUN_ID
)
insert into #FUN_Names
([Level_Label],[FUN_Name],[FUN_Level],[FUN_ID])
(select [Level_Label],[FUN_Name],[FUN_Level],[FUN_ID] from cte
where exists (select FUA_isActive from FunctionalUnitsActive where FUA_isActive=1))
return
RETURN
END
GO
Any suggestions or anything that can hep me?
Ok I've added fx_GetFUNStructureCTE_Asc

Considering the informations you gave, besides what #Lamak said, it also may depend on what the the field ISD_FUNID values has on [dev_SAB_EM].[dbo].[SiteDetails] table. If they are all the same on every record then there's no problem with your code...
But, it's a basic assumption...
And, assuming what you said on your comment as the value of ISD_FUNID being NULL, what may be happening is this: when all the value of the field FUN_ID are 0 on table #Temp1 what will happen when you execute the query on the function [dev_SAB_Man].[dbo].[fx_GetFUNStructureCTE_Asc] is that all rows will loop while the assignment occurs, setting the variables values to the last one returned by the function.
It'll make all the variables values be the same for all #tmp2 rows. You may need to improve the function call to return just one value.

Related

There is already an object named '#BaseData' in the database

Below is a snippet of my code.
I am wanting to filter my data based upon a variable.
When I try to run the code, it returns an error of "There is already an object named '#BaseData' in the database.". I am not sure as to why this is the case; I have put extra checks within the IF statements to drop the temp table if it already exists but to no avail.
Are you able to help or provide an alternative solution please?
DECLARE #Variable AS VARCHAR(20) = 'Example1'
IF OBJECT_ID(N'TEMPDB..#BaseData') IS NOT NULL
DROP TABLE #BaseData
IF #Variable = 'Example1'
BEGIN
SELECT
*
INTO
#BaseData
FROM
[Database].[schema].[table]
END
IF #Variable = 'Example2'
BEGIN
SELECT
*
INTO
#BaseData
FROM
[Database].[schema].[table]
WHERE
[column] = 1
END
IF #Variable = 'Example3'
BEGIN
SELECT
*
INTO
#BaseData
FROM
[Database].[schema].[table]
WHERE
[column] = 0
END
While code is compiled by SQL, creation of same #table is found in each condition so it doesn't work.
One possible solution would be to create table and than insert data conditionally.
-- DROP TEMP TABLE IF EXISTS
IF OBJECT_ID(N'TEMPDB..#BaseData') IS NOT NULL
DROP TABLE #BaseData
GO
-- CRATE TEMP TABLE WITH TempId, AND SAME STRUCTURE AS YOUR TABLE
SELECT TOP 0 CONVERT(INT, 0)TempId, * INTO #BaseData FROM TestTable
-- DECLARE VARIABLE
DECLARE #Variable AS VARCHAR(20)= 'Example1'
-- INSERT DATA IN TABLE DEPENDING FROM CONDITION
IF (#Variable = 'Example1')
BEGIN
INSERT INTO #BaseData SELECT * FROM TestTable
END
IF (#Variable = 'Example2')
BEGIN
INSERT INTO #BaseData SELECT * FROM TestTable WHERE Id = 1
END
IF (#Variable = 'Example3')
BEGIN
INSERT INTO #BaseData SELECT * FROM TestTable WHERE Id = 2
END

SQL dynamic columns and Update multiple columns

I have a table UserPermission which has a number of columns of TINYINT type. e.g Read, Write, Update, Delete, Access etc.
I get three parameters in the stored procedure: #UserId, #ColNames, #ColValues where #ColNames and #ColValues are comma separated values.
How can I insert or update the table row (if already exists) with the passed column names and corresponding values.
I try to write the dynamic query which runs fine for INSERT but I was unable to write the UPDATE query dynamically with each column and its value to be concatenate.
Any response would be appreciated
Thanks in advance.
This is a somewhat dirty way to do what you require. However, if you create the following Stored Procedure:
CREATE FUNCTION [dbo].[stringSplit]
(
#String NVARCHAR(4000),
#Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(#Delimiter,#String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(#Delimiter,#String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(#String,stpos,COALESCE(NULLIF(endpos,0),LEN(#String)+1)-stpos)
FROM Split
)
You can then use that Procedure to join the data together:
DECLARE #TotalCols INT
DECLARE #TotalVals INT
SET #TotalCols = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('department, teamlead', ',')
);
SET #TotalVals = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('IT, Bob', ',')
);
IF #TotalCols = #TotalVals
BEGIN
IF OBJECT_ID('tempdb..#temptable') IS NOT NULL
DROP TABLE #temptable
CREATE TABLE #temptable (
ColName VARCHAR(MAX) NULL
,ColValue VARCHAR(MAX) NULL
)
INSERT INTO #temptable
SELECT a.DATA
,b.DATA
FROM dbo.stringSplit('department, teamlead', ',') AS a
INNER JOIN dbo.stringSplit('IT, Bob', ',') AS b ON a.Id = b.Id
SELECT *
FROM #temptable;
END
It's not very efficient, but it will bring you the desired results.
You can then use the temp table to update, insert and delete as required.
Instead of having a comma delimited list I would create a separate parameter for each Column and make its default value to NULL and in the code update nothing if its null or insert 0. Something like this....
CREATE PROCEDURE usp_UserPermissions
#UserID INT
,#Update INT = NULL --<-- Make default values NULL
,#Delete INT = NULL
,#Read INT = NULL
,#Write INT = NULL
,#Access INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #t TABLE (UserID INT, [Update] INT,[Read] INT
,[Write] INT,[Delete] INT,[Access] INT)
INSERT INTO #t (Userid, [Update],[Read],[Write],[Delete],[Access])
VALUES (#UserID , #Update , #Read, #Write , #Delete, #Access)
IF EXISTS (SELECT 1 FROM UserPermission WHERE UserID = #UserID)
BEGIN
UPDATE up -- Only update if a value was provided else update to itself
SET up.[Read] = ISNULL(t.[Read] , up.[Read])
,up.[Write] = ISNULL(t.[Write] , up.[Write])
,up.[Update] = ISNULL(t.[Update] , up.[Update])
,up.[Delete] = ISNULL(t.[Delete] , up.[Delete])
,up.[Access] = ISNULL(t.[Access] , up.[Access])
FROM UserPermission up
INNER JOIN #t t ON up.UserID = t.UserID
END
ELSE
BEGIN
-- if already no row exists for that User add a row
-- If no value was passed for a column add 0 as default
INSERT INTO UserPermission (Userid, [Update],[Read],[Write],[Delete],[Access])
SELECT Userid
, ISNULL([Update], 0)
, ISNULL([Read], 0)
, ISNULL([Write], 0)
, ISNULL([Delete], 0)
, ISNULL([Access], 0)
FROM #t
END
END

Function return table variable

I'm trying to create a function that return a table variable.So firstly i get data from Table1 and put it in another table variable. Here i want check if this variable isempty the function return the parameter result else return the result of the table variable
The function script is bellow :
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
DECLARE #CodeM nvarchar(400)
DECLARE #imax INT SET #imax = ##ROWCOUNT
DECLARE #i INT SET #i = 1
DECLARE #SelectDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
IF(EXISTS (SELECT 1 FROM #SelectDAT))
BEGIN
WHILE (#i <= #imax)
BEGIN
SELECT #CodeM = Code FROM #SelectDAT WHERE RowID = #i
INSERT INTO #VirtualDAT(Code) VALUES (#CodeM)
SET #i = #i + 1
END
END
ELSE
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
RETURN
END
So this script works without put it inside function.
And i test this function like this :SELECT * FROM dbo.CodeFunc( 'toto',Default ) the result is :
IF(EXISTS (SELECT 1 FROM #SelectDAT)) no record returned
esle the result is ok
As VR46 says. The ##ROWCOUNT will be set to 0 because there is no query before it. Any code executing in a function happens as a seperate set of queries. It was probably returning a value outside the function because you had previously used the query window for another unrelated query
You could re-factor this function quite dramatically. Look below, ##ROWCOUNT will work here as it is just after the insert query and will definitely have a value based on the insert.
I have not been able to test this, but I think something like this should do the same job.
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
insert into #VirtualDAT
Select Code from table1 where table1.id = 41
if ##ROWCOUNT = 0
begin
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
end
RETURN
END
Since you are assigning #imax with ##ROWCOUNT right after declaration of variable will be initialized with zero.
From MSDN ##ROWCOUNT
Returns the number of rows affected by the last statement.
If am not wrong you need to assign value to #imax after the insert into..select query.
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
SET #imax= ##ROWCOUNT
You can do the same in SET BASED APPROACH without using while loop.
CREATE FUNCTION Codefunc (#Code NVARCHAR(4),
#Table NVARCHAR(40) = '')
returns #VirtualDAT TABLE (
rowid INT IDENTITY ( 1, 1 ),
code NVARCHAR(400))
AS
BEGIN
IF EXISTS (SELECT code
FROM table1
WHERE table1.id = 41)
BEGIN
INSERT INTO #VirtualDAT
(code)
SELECT code
FROM table1
WHERE table1.id = 41
END
ELSE
INSERT INTO #VirtualDAT
(code)
VALUES (#Code)
RETURN
END

SQL server stored procedure return a table

I have a stored procedure that takes in two parameters. I can execute it successfully in Server Management Studio. It shows me the results which are as I expect. However it also returns a Return Value.
It has added this line,
SELECT 'Return Value' = #return_value
I would like the stored procedure to return the table it shows me in the results not the return value as I am calling this stored procedure from MATLAB and all it returns is true or false.
Do I need to specify in my stored procedure what it should return? If so how do I specify a table of 4 columns (varchar(10), float, float, float)?
A procedure can't return a table as such. However you can select from a table in a procedure and direct it into a table (or table variable) like this:
create procedure p_x
as
begin
declare #t table(col1 varchar(10), col2 float, col3 float, col4 float)
insert #t values('a', 1,1,1)
insert #t values('b', 2,2,2)
select * from #t
end
go
declare #t table(col1 varchar(10), col2 float, col3 float, col4 float)
insert #t
exec p_x
select * from #t
I do this frequently using Table Types to ensure more consistency and simplify code. You can't technically return "a table", but you can return a result set and using INSERT INTO .. EXEC ... syntax, you can clearly call a PROC and store the results into a table type. In the following example I'm actually passing a table into a PROC along with another param I need to add logic, then I'm effectively "returning a table" and can then work with that as a table variable.
/****** Check if my table type and/or proc exists and drop them ******/
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'returnTableTypeData')
DROP PROCEDURE returnTableTypeData
GO
IF EXISTS (SELECT * FROM sys.types WHERE is_table_type = 1 AND name = 'myTableType')
DROP TYPE myTableType
GO
/****** Create the type that I'll pass into the proc and return from it ******/
CREATE TYPE [dbo].[myTableType] AS TABLE(
[someInt] [int] NULL,
[somenVarChar] [nvarchar](100) NULL
)
GO
CREATE PROC returnTableTypeData
#someInputInt INT,
#myInputTable myTableType READONLY --Must be readonly because
AS
BEGIN
--Return the subset of data consistent with the type
SELECT
*
FROM
#myInputTable
WHERE
someInt < #someInputInt
END
GO
DECLARE #myInputTableOrig myTableType
DECLARE #myUpdatedTable myTableType
INSERT INTO #myInputTableOrig ( someInt,somenVarChar )
VALUES ( 0, N'Value 0' ), ( 1, N'Value 1' ), ( 2, N'Value 2' )
INSERT INTO #myUpdatedTable EXEC returnTableTypeData #someInputInt=1, #myInputTable=#myInputTableOrig
SELECT * FROM #myUpdatedTable
DROP PROCEDURE returnTableTypeData
GO
DROP TYPE myTableType
GO
Consider creating a function which can return a table and be used in a query.
https://msdn.microsoft.com/en-us/library/ms186755.aspx
The main difference between a function and a procedure is that a function makes no changes to any table. It only returns a value.
In this example I'm creating a query to give me the counts of all the columns in a given table which aren't null or empty.
There are probably many ways to clean this up. But it illustrates a function well.
USE Northwind
CREATE FUNCTION usp_listFields(#schema VARCHAR(50), #table VARCHAR(50))
RETURNS #query TABLE (
FieldName VARCHAR(255)
)
BEGIN
INSERT #query
SELECT
'SELECT ''' + #table+'~'+RTRIM(COLUMN_NAME)+'~''+CONVERT(VARCHAR, COUNT(*)) '+
'FROM '+#schema+'.'+#table+' '+
' WHERE isnull("'+RTRIM(COLUMN_NAME)+'",'''')<>'''' UNION'
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #table and TABLE_SCHEMA = #schema
RETURN
END
Then executing the function with
SELECT * FROM usp_listFields('Employees')
produces a number of rows like:
SELECT 'Employees~EmployeeID~'+CONVERT(VARCHAR, COUNT(*)) FROM dbo.Employees WHERE isnull("EmployeeID",'')<>'' UNION
SELECT 'Employees~LastName~'+CONVERT(VARCHAR, COUNT(*)) FROM dbo.Employees WHERE isnull("LastName",'')<>'' UNION
SELECT 'Employees~FirstName~'+CONVERT(VARCHAR, COUNT(*)) FROM dbo.Employees WHERE isnull("FirstName",'')<>'' UNION
You can use an out parameter instead of the return value if you want both a result set and a return value
CREATE PROCEDURE proc_name
#param int out
AS
BEGIN
SET #param = value
SELECT ... FROM [Table] WHERE Condition
END
GO
I had a similar situation and solved by using a temp table inside the procedure, with the same fields being returned by the original Stored Procedure:
CREATE PROCEDURE mynewstoredprocedure
AS
BEGIN
INSERT INTO temptable (field1, field2)
EXEC mystoredprocedure #param1, #param2
select field1, field2 from temptable
-- (mystoredprocedure returns field1, field2)
END
The Status Value being returned by a Stored Procedure can only be an INT datatype. You cannot return other datatypes in the RETURN statement.
From Lesson 2: Designing Stored Procedures:
Every stored procedure can return an integer value known as the
execution status value or return code.
If you still want a table returned from the SP, you'll either have to work the record set returned from a SELECT within the SP or tie into an OUTPUT variable that passes an XML datatype.
HTH,
John
Though this question is very old but as a new in Software Development I can't stop my self to share what I have learnt :D
Creation of Stored Procedure:
CREATE PROC usp_ValidateUSer
(
#UserName nVARCHAR(50),
#Password nVARCHAR(50)
)
AS
BEGIN
IF EXISTS(SELECT '#' FROM Users WHERE Username=#UserName AND Password=#Password)
BEGIN
SELECT u.UserId, u.Username, r.UserRole
FROM Users u
INNER JOIN UserRoles r
ON u.UserRoleId=r.UserRoleId
END
END
Execution of Stored Procedure:
(If you want to test the execution of Stored Procedure in SQL)
EXEC usp_ValidateUSer #UserName='admin', #Password='admin'
The Output:
create procedure PSaleCForms
as
begin
declare
#b varchar(9),
#c nvarchar(500),
#q nvarchar(max)
declare #T table(FY nvarchar(9),Qtr int,title nvarchar (max),invoicenumber nvarchar(max),invoicedate datetime,sp decimal 18,2),grandtotal decimal(18,2))
declare #data cursor
set #data= Cursor
forward_only static
for
select x.DBTitle,y.CurrentFinancialYear from [Accounts Manager].dbo.DBManager x inner join [Accounts Manager].dbo.Accounts y on y.DBID=x.DBID where x.cfy=1
open #data
fetch next from #data
into #c,#b
while ##FETCH_STATUS=0
begin
set #q=N'Select '''+#b+''' [fy], case cast(month(i.invoicedate)/3.1 as int) when 0 then 4 else cast(month(i.invoicedate)/3.1 as int) end [Qtr], l.title,i.invoicenumber,i.invoicedate,i.sp,i.grandtotal from ['+#c+'].dbo.invoicemain i inner join ['+#c+'].dbo.ledgermain l on l.ledgerid=i.ledgerid where (sp=0 or stocktype=''x'') and invoicetype=''DS'''
insert into #T exec [master].dbo.sp_executesql #q
fetch next from #data
into #c,#b
end
close #data
deallocate #data
select * from #T
return
end
Here's an example of a SP that both returns a table and a return value. I don't know if you need the return the "Return Value" and I have no idea about MATLAB and what it requires.
CREATE PROCEDURE test
AS
BEGIN
SELECT * FROM sys.databases
RETURN 27
END
--Use this to test
DECLARE #returnval int
EXEC #returnval = test
SELECT #returnval

Dynamic sql using table variable -TSQL

My problem is using a table variable in a exec.
declare #sort_col nvarchar(1000) = 'itm_id'
declare #sort_dir nvarchar(4) = 'desc'
declare #filters nvarchar(1000) = ' and itm_name like ''%aa%'''
declare #temp table
(
itm_id int
)
insert into #temp
EXEC('select itm_id from Tblitm where itm_name not like ''%aa%''')
EXEC('select * from (select (ROW_NUMBER() OVER (ORDER BY '+#sort_col+' '+#sort_dir+')) row_num, * FROM (select itm_id, itm_name,
dbo.fnItmsHistory(itm_id) itm_history
from dbo.Tblitm as itm
left outer join '+#temp+' as temp on itm.itm_id = temp.itm_id
where itm_id=itm_id and temp.itm_id = null '+#filters+') as x) as tmp')
It says Must declare the scalar variable "#temp" when the temp table is declared i tried using original temp table and it worked, but i had problems when trying to update my entity model.So is there any solution for this problem?
Note:
I must use exec because in filters i store string for the where clause.
Try moving the table variable inside the dynamic statement.
EXEC('
declare #temp table
(
itm_id int
)
insert into #temp
select itm_id from Tblitm where itm_name not like ''%aa%''
select * from (select (ROW_NUMBER() OVER (ORDER BY '+#sort_col+' '+#sort_dir+')) row_num, * FROM (select itm_id, itm_name,
dbo.fnItmsHistory(itm_id) itm_history
from dbo.Tblitm as itm
left outer join #temp as temp on itm.itm_id = temp.itm_id
where itm_id=itm_id and temp.itm_id = null '+#filters+') as x) as tmp')
For solution i had to use a temp table and then on the start of my stored procedure i used the if condition from the EF can't infer return schema from Stored Procedure selecting from a #temp table anwser.
It's the best solution for this scenario i think.