How do i use rowcount in Function in sqlserver? - sql

I have faced a problem using rowcount in function in sql server 2000.
It shows an error like Invalid use of 'UNKNOWN TOKEN' within a function.
MY function is like this.
ALTER Function fnc_GetOpenShiftWorkID (#EMP_ID int,#Counter int,#date Datetime) returns int as
BEGIN
SET ROWCOUNT #Counter
declare #result int
if exists(select * from tbl_org_workinghrs WHERE EMP_ID=#EMP_ID and SDATE=#DATE)
BEGIN
select #result= WORK_ID
from tbl_org_working_hrs work_hrs
inner join tbl_org_shift_group sgroup on sgroup.WH_ID=work_hrs.WORK_ID
inner join tbl_org_workinghrs workhrs on workhrs.GROUP_ID=sgroup.GROUP_ID
WHERE EMP_ID=#EMP_ID
and SDATE=#DATE
order by
IN_START
END
ELSE
BEGIN
if exists(select * from tbl_org_workinghrs where EMP_ID=0)
BEGIN
select #result=WORK_ID
from tbl_org_working_hrs
WHERE IS_DEFAULTSHIFT=1
END
END
return #result
END

You want to get the value of the n'th row ordered by IN_START.
From SQL Server 2005 later you could use top(n) or row_number().
In SQL Server 2000 you can use a table variable with an identity ID field as a temp storage.
Something like this.
declare #T table
(
ID int identity,
WORK_ID int
)
insert into #T (WORK_ID)
select WORK_ID
from tbl_org_working_hrs work_hrs
inner join tbl_org_shift_group sgroup
on sgroup.WH_ID=work_hrs.WORK_ID
inner join tbl_org_workinghrs workhrs
on workhrs.GROUP_ID=sgroup.GROUP_ID
where EMP_ID=#EMP_ID and
SDATE=#DATE
order by IN_START
select #result = WORK_ID
from #T
where ID = #Counter

Related

How to create procedure with multiple string select query in sql?

I want to create procedure with multiple string select query.I want to insert data to table variable and join that temp table with other table.
I don't want to create temp table as real tables. I want to insert data to memory temp table.
Here is my procedure,
CREATE PROCEDURE sp_TempBatch
AS
DECLARE #TempBatchSerial TABLE
(
ID int,
Name nvarchar(200),
StockType nvarchar(50),
ItemNo nvarchar(50)
)
DECLARE #TempQuery as nvarchar(MAX)='',
#VendorQuery as nvarchar(MAX)=''
BEGIN
SET #TempQuery='SELECT ID,Name,'
IF StockType = '1'
BEGIN
SET #TempQuery += ' ''Batch'' as StockType,'
END
ELSE
BEGIN
SET #TempQuery += ' ''Serial'' as StockType,'
END
SET #TempQuery += 'ItemNo INTO #TempBatchSerial
FROM Stock'
EXEC (#TempQuery)
SET #VendorQuery+=' SELECT #TempBatchSerial.* FROM #TempBatchSerial
INNER JOIN Vendor
ON #TempBatchSerial.ID = Vendor.ID
INNER JOIN Partner
ON Vendor.parentid = Partner.syskey'
EXEC (#VendorQuery)
END
When execute procedure show error message of Must declare the table variable "#TempBatchSerial"
You have to refer to #tempBatchSerial via an Alias
That's the only way #tempTables can be referred or linked to.
SELECT T.* FROM #TempBatchSerial T
INNER JOIN Vendor
ON T.ID = Vendor.ID
INNER JOIN Partner
ON Vendor.parentid = Partner.syskey
If that doesn't work you can try to put the #tempTable in the #vendorQuery text.

Looking for missing gaps for getting "Only one expression can be specified in the select list when the subquery is not introduced with EXISTS."

I have the following query to find missing gaps in the sort for each ModelID but I keep getting the following error and don't know why.
What I'm doing is in my first loop I am looping through the modelID's and in the inner loop I am looking for the missing gaps in the siSort column for that modelID and putting that into a temp table.
Msg 116, Level 16, State 1, Line 27
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
USE crm
GO
BEGIN
DECLARE #ID INT
DECLARE #MAXID INT
DECLARE #COUNT INT
DECLARE #iCustomListModelID INT
DECLARE #iCustomFieldID INT
DECLARE #MissingIds TABLE (ID INT)
DECLARE #Output TABLE (iCustomListModelID INT, siSort INT, iListItemID INT)
-- SELECT ALL DISTINCT ICustomListModelID's FROM CustomList Table
SELECT DISTINCT cl.iCustomListModelID
INTO #DistinctModelIDs
FROM dbo.CustomListModel clm
INNER JOIN dbo.CustomListType clt ON clm.iCustomListTypeID = clt.iCustomListTypeID
AND clt.vchCustomListTypeDescription = N'Household Custom Field'
INNER JOIN dbo.CustomList cl ON clm.iCustomListModelID = cl.iCustomListModelID
INNER JOIN dbo.CustomField cf ON cl.iListItemID = cf.iCustomFieldID
ORDER BY cl.iCustomListModelID
-- Get iCustomFieldID to insert into iListItemID
SET #iCustomFieldID = (SELECT * FROM dbo.CustomField cf WHERE vchLabel = '')
-- Begin Outer loop to go through each iCustomListModelID
WHILE (SELECT COUNT(iCustomListModelID) AS Total FROM #DistinctModelIDs) > 0
BEGIN
-- GRAB THE NEXT iCustomListModelID
SELECT #iCustomListModelID = (SELECT TOP 1 iCustomListModelID FROM #DistinctModelIDs);
DROP TABLE #List
SELECT siSort INTO #List FROM CustomList WHERE iCustomListModelID = #iCustomListModelID
SELECT #MAXID = siSort FROM dbo.CustomList WHERE iCustomListModelID = #iCustomListModelID
SET #ID = 1;
-- Inner loop to go through the missing gaps in siSort
WHILE #ID <= #MAXID
BEGIN
IF NOT EXISTS (
SELECT 'X' FROM #List WHERE siSort = #ID
)
INSERT INTO #MissingIDs (ID)
VALUES (#ID)
--INSERT THE MISSING ID INTO #outputTable Table
INSERT INTO #Output (iCustomListModelID, siSort, iListItemID)
VALUES (#iCustomListModelID, #ID, #iCustomFieldID)
SET #ID = #ID + 1;
END;
-- DELETE CURRENT iCustomListModelID
DELETE FROM #DistinctModelIDs WHERE iCustomListModelID = #iCustomListModelID
END
SELECT * FROM #Output
END;
One possibility is that the issue is this line:
SET #iCustomFieldID = (SELECT * FROM dbo.CustomField cf WHERE vchLabel = '')
If dbo.CustomerField doesn't have exactly one column (more than one column seems likely because vchLabel is already one column in the table), then this will generate an error of that type.

get error in sql stored procedure table type

i have this procedure and i want insert list of value into table,and i want check duplicate value and return this but i get this error
ERROR:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
ALTER PROCEDURE [dbo].[insertData]
#Value insertTbl READONLY,
#Result INT OUTPUT
AS
BEGIN
BEGIN TRY
IF (SELECT COUNT(Id)
FROM #Values
WHERE Id IN (SELECT Id
FROM [dbo].[username])) = 0
BEGIN
INSERT INTO dbo.username ( Id, NAME )
SELECT Id,
NAME
FROM #Values
WHERE Id NOT IN (SELECT Id
FROM [dbo].[username])
SELECT #Result = 101
END
ELSE
SELECT #Result = (
SELECT Id
FROM #Values
WHERE Id IN (SELECT Id
FROM [dbo].[username])
)
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
END CATCH
END
Please try this.
I have changed the return type to VARCHAR. That would return a CSV (e.g. 1,3,9...)
The other option is to return a result set. For this replace SELECT #Result = to INSERT INTO #Result...
Hope this helps.
ALTER PROCEDURE [dbo].[insertData]
#Value insertTbl READONLY,
#Result VARCHAR(500) OUTPUT
AS
BEGIN
BEGIN TRY
IF (SELECT COUNT(Id)
FROM #Values
WHERE Id IN (SELECT Id
FROM [dbo].[username])
) = 0
BEGIN
INSERT INTO dbo.username (Id,NAME)
SELECT Id,
NAME
FROM #Values
WHERE Id NOT IN (SELECT Id
FROM [dbo].[username])
SELECT #Result = 101
END
ELSE
BEGIN
SELECT #Result = (SELECT STUFF((SELECT ', ' + CAST(ID AS VARCHAR(5))
FROM #Values
WHERE Id IN (SELECT Id
FROM [dbo].[username])
ORDER BY Id FOR XML PATH('')),1,2,''))
END
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
END CATCH
END
Probably this line
SELECT #Result=(SELECT Id FROM #Values WHERE Id IN (SELECT Id FROM [dbo].[username]))
return more than one row. You should change the query to return only one value as below
SELECT #Result=(SELECT top 1 Id FROM #Values WHERE Id IN (SELECT Id FROM [dbo].[username]))
or you should change your logic. Alternative way is to use temprary table to return listo of ID. Try below solution
-- create temporary table before (!) creating procedure
create table #Resulttab
(
Result int
)
go
ALTER PROCEDURE [dbo].[insertData]
#Value insertTbl READONLY,
#Result INT OUTPUT -- in this solution I think you don't need this parameter
AS
BEGIN
BEGIN TRY
IF (SELECT COUNT(Id)
FROM #Values
WHERE Id IN (SELECT Id
FROM [dbo].[username])) = 0
BEGIN
INSERT INTO dbo.username ( Id, NAME )
SELECT Id,
NAME
FROM #Values
WHERE Id NOT IN (SELECT Id
FROM [dbo].[username])
SELECT #Result = 101
END
ELSE
insert into #Resulttab
SELECT Id
FROM #Values
WHERE Id IN (SELECT Id
FROM [dbo].[username])
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
END CATCH
END
go
You can use it that way:
-- create temporary table before run procedure
create table #Resulttab
(
Result int
)
-- run procedure with parameters
exec insertData ...
--after run procedure you can check list of your IDs
select Result from #Resulttab

sql: my data repeats itself

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.

Delete duplicate records in SQL Server?

Consider a column named EmployeeName table Employee. The goal is to delete repeated records, based on the EmployeeName field.
EmployeeName
------------
Anand
Anand
Anil
Dipak
Anil
Dipak
Dipak
Anil
Using one query, I want to delete the records which are repeated.
How can this be done with TSQL in SQL Server?
You can do this with window functions. It will order the dupes by empId, and delete all but the first one.
delete x from (
select *, rn=row_number() over (partition by EmployeeName order by empId)
from Employee
) x
where rn > 1;
Run it as a select to see what would be deleted:
select *
from (
select *, rn=row_number() over (partition by EmployeeName order by empId)
from Employee
) x
where rn > 1;
Assuming that your Employee table also has a unique column (ID in the example below), the following will work:
delete from Employee
where ID not in
(
select min(ID)
from Employee
group by EmployeeName
);
This will leave the version with the lowest ID in the table.
Edit
Re McGyver's comment - as of SQL 2012
MIN can be used with numeric, char, varchar, uniqueidentifier, or datetime columns, but not with bit columns
For 2008 R2 and earlier,
MIN can be used with numeric, char, varchar, or datetime columns, but not with bit columns (and it also doesn't work with GUID's)
For 2008R2 you'll need to cast the GUID to a type supported by MIN, e.g.
delete from GuidEmployees
where CAST(ID AS binary(16)) not in
(
select min(CAST(ID AS binary(16)))
from GuidEmployees
group by EmployeeName
);
SqlFiddle for various types in Sql 2008
SqlFiddle for various types in Sql 2012
You could try something like the following:
delete T1
from MyTable T1, MyTable T2
where T1.dupField = T2.dupField
and T1.uniqueField > T2.uniqueField
(this assumes that you have an integer based unique field)
Personally though I'd say you were better off trying to correct the fact that duplicate entries are being added to the database before it occurs rather than as a post fix-it operation.
DELETE
FROM MyTable
WHERE ID NOT IN (
SELECT MAX(ID)
FROM MyTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)
WITH TempUsers (FirstName, LastName, duplicateRecordCount)
AS
(
SELECT FirstName, LastName,
ROW_NUMBER() OVER (PARTITIONBY FirstName, LastName ORDERBY FirstName) AS duplicateRecordCount
FROM dbo.Users
)
DELETE
FROM TempUsers
WHERE duplicateRecordCount > 1
WITH CTE AS
(
SELECT EmployeeName,
ROW_NUMBER() OVER(PARTITION BY EmployeeName ORDER BY EmployeeName) AS R
FROM employee_table
)
DELETE CTE WHERE R > 1;
The magic of common table expressions.
Try
DELETE
FROM employee
WHERE rowid NOT IN (SELECT MAX(rowid) FROM employee
GROUP BY EmployeeName);
If you're looking for a way to remove duplicates, yet you have a foreign key pointing to the table with duplicates, you could take the following approach using a slow yet effective cursor.
It will relocate the duplicate keys on the foreign key table.
create table #properOlvChangeCodes(
id int not null,
name nvarchar(max) not null
)
DECLARE #name VARCHAR(MAX);
DECLARE #id INT;
DECLARE #newid INT;
DECLARE #oldid INT;
DECLARE OLVTRCCursor CURSOR FOR SELECT id, name FROM Sales_OrderLineVersionChangeReasonCode;
OPEN OLVTRCCursor;
FETCH NEXT FROM OLVTRCCursor INTO #id, #name;
WHILE ##FETCH_STATUS = 0
BEGIN
-- determine if it should be replaced (is already in temptable with name)
if(exists(select * from #properOlvChangeCodes where Name=#name)) begin
-- if it is, finds its id
Select top 1 #newid = id
from Sales_OrderLineVersionChangeReasonCode
where Name = #name
-- replace terminationreasoncodeid in olv for the new terminationreasoncodeid
update Sales_OrderLineVersion set ChangeReasonCodeId = #newid where ChangeReasonCodeId = #id
-- delete the record from the terminationreasoncode
delete from Sales_OrderLineVersionChangeReasonCode where Id = #id
end else begin
-- insert into temp table if new
insert into #properOlvChangeCodes(Id, name)
values(#id, #name)
end
FETCH NEXT FROM OLVTRCCursor INTO #id, #name;
END;
CLOSE OLVTRCCursor;
DEALLOCATE OLVTRCCursor;
drop table #properOlvChangeCodes
delete from person
where ID not in
(
select t.id from
(select min(ID) as id from person
group by email
) as t
);
Please see the below way of deletion too.
Declare #Employee table (EmployeeName varchar(10))
Insert into #Employee values
('Anand'),('Anand'),('Anil'),('Dipak'),
('Anil'),('Dipak'),('Dipak'),('Anil')
Select * from #Employee
Created a sample table named #Employee and loaded it with given data.
Delete aliasName from (
Select *,
ROW_NUMBER() over (Partition by EmployeeName order by EmployeeName) as rowNumber
From #Employee) aliasName
Where rowNumber > 1
Select * from #Employee
Result:
I know, this is asked six years ago, posting just incase it is helpful for anyone.
Here's a nice way of deduplicating records in a table that has an identity column based on a desired primary key that you can define at runtime. Before I start I'll populate a sample data set to work with using the following code:
if exists (select 1 from sys.all_objects where type='u' and name='_original')
drop table _original
declare #startyear int = 2017
declare #endyear int = 2018
declare #iterator int = 1
declare #income money = cast((SELECT round(RAND()*(5000-4990)+4990 , 2)) as money)
declare #salesrepid int = cast(floor(rand()*(9100-9000)+9000) as varchar(4))
create table #original (rowid int identity, monthyear varchar(max), salesrepid int, sale money)
while #iterator<=50000 begin
insert #original
select (Select cast(floor(rand()*(#endyear-#startyear)+#startyear) as varchar(4))+'-'+ cast(floor(rand()*(13-1)+1) as varchar(2)) ), #salesrepid , #income
set #salesrepid = cast(floor(rand()*(9100-9000)+9000) as varchar(4))
set #income = cast((SELECT round(RAND()*(5000-4990)+4990 , 2)) as money)
set #iterator=#iterator+1
end
update #original
set monthyear=replace(monthyear, '-', '-0') where len(monthyear)=6
select * into _original from #original
Next I'll create a Type called ColumnNames:
create type ColumnNames AS table
(Columnnames varchar(max))
Finally I will create a stored proc with the following 3 caveats:
1. The proc will take a required parameter #tablename that defines the name of the table you are deleting from in your database.
2. The proc has an optional parameter #columns that you can use to define the fields that make up the desired primary key that you are deleting against. If this field is left blank, it is assumed that all the fields besides the identity column make up the desired primary key.
3. When duplicate records are deleted, the record with the lowest value in it's identity column will be maintained.
Here is my delete_dupes stored proc:
create proc delete_dupes (#tablename varchar(max), #columns columnnames readonly)
as
begin
declare #table table (iterator int, name varchar(max), is_identity int)
declare #tablepartition table (idx int identity, type varchar(max), value varchar(max))
declare #partitionby varchar(max)
declare #iterator int= 1
if exists (select 1 from #columns) begin
declare #columns1 table (iterator int, columnnames varchar(max))
insert #columns1
select 1, columnnames from #columns
set #partitionby = (select distinct
substring((Select ', '+t1.columnnames
From #columns1 t1
Where T1.iterator = T2.iterator
ORDER BY T1.iterator
For XML PATH ('')),2, 1000) partition
From #columns1 T2 )
end
insert #table
select 1, a.name, is_identity from sys.all_columns a join sys.all_objects b on a.object_id=b.object_id
where b.name = #tablename
declare #identity varchar(max)= (select name from #table where is_identity=1)
while #iterator>=0 begin
insert #tablepartition
Select distinct case when #iterator=1 then 'order by' else 'over (partition by' end ,
substring((Select ', '+t1.name
From #table t1
Where T1.iterator = T2.iterator and is_identity=#iterator
ORDER BY T1.iterator
For XML PATH ('')),2, 5000) partition
From #table T2
set #iterator=#iterator-1
end
declare #originalpartition varchar(max)
if #partitionby is null begin
select #originalpartition = replace(b.value+','+a.type+a.value ,'over (partition by','') from #tablepartition a cross join #tablepartition b where a.idx=2 and b.idx=1
select #partitionby = a.type+a.value+' '+b.type+a.value+','+b.value+') rownum' from #tablepartition a cross join #tablepartition b where a.idx=2 and b.idx=1
end
else
begin
select #originalpartition=b.value +','+ #partitionby from #tablepartition a cross join #tablepartition b where a.idx=2 and b.idx=1
set #partitionby = (select 'OVER (partition by'+ #partitionby + ' ORDER BY'+ #partitionby + ','+b.value +') rownum'
from #tablepartition a cross join #tablepartition b where a.idx=2 and b.idx=1)
end
exec('select row_number() ' + #partitionby +', '+#originalpartition+' into ##temp from '+ #tablename+'')
exec(
'delete a from _original a
left join ##temp b on a.'+#identity+'=b.'+#identity+' and rownum=1
where b.rownum is null')
drop table ##temp
end
Once this is complied, you can delete all your duplicate records by running the proc. To delete dupes without defining a desired primary key use this call:
exec delete_dupes '_original'
To delete dupes based on a defined desired primary key use this call:
declare #table1 as columnnames
insert #table1
values ('salesrepid'),('sale')
exec delete_dupes '_original' , #table1