SQL Server : function not working in trigger - sql-server

I have trigger on insert like that :
ALTER TRIGGER [dbo].[trTblNameSetRefNo]
ON [dbo].[TblName]
AFTER INSERT
AS BEGIN
UPDATE TblName
SET RefNumber = dbo.GetNextRefNo(i.SomeField)
FROM TblName
INNER JOIN inserted i on i.ID = TblName.ID
END
But it doesn't work. When I 'hardcode' field RefNumber , for example : SET RefNumber = 'test', it works correctly.
Also when I call function outside of the trigger, it return proper result.
And this is my function that must return value in trigger :
ALTER FUNCTION [dbo].[GetNextRefNo]
(#ValueField INT)
RETURNS NVARCHAR(250)
AS
BEGIN
DECLARE #lastId INT;
DECLARE #result NVARCHAR(25);
DECLARE #CurrentIdentifier NVARCHAR(25);
SELECT TOP 1
#lastId = CAST(Substring(RefNumber, Charindex('-', RefNumber) + 4, Len(RefNumber )) AS INT) + 1
FROM
TblName
ORDER BY
ID DESC
IF ##ROWCOUNT < 1 --if empty table , set start number
BEGIN
SET #lastId = 1000
END
SELECT #CurrentIdentifier = 'SIT'
SET #result = #CurrentIdentifier + '-' + Substring ( Cast(Year(Getdate()) AS NVARCHAR), 3, 2) + '-' + Cast(#lastId AS NVARCHAR)
RETURN #result
END
Any ideas what I'm doing wrong?

Your function returns what is probably an incorrect result when RefNumber = '' and it returns NULL when RefNumber = NULL, as would happen for a newly inserted record.
The design of the above code is beyond bad.
Suggested approach:
Use ID field as the counter and generate RefNumber based on that, while concatating Year as needed.
Example function:
ALTER FUNCTION [dbo].[GetNextRefNo] (#ID INT)
returns NVARCHAR(250)
AS
BEGIN
DECLARE #lastId INT;
DECLARE #result NVARCHAR(25);
DECLARE #CurrentIdentifier NVARCHAR(25);
SELECT #CurrentIdentifier = 'SIT'
SET #ID = #ID + 1000
SET #result = #CurrentIdentifier + '-' + Substring ( Cast(Year(Getdate()) AS NVARCHAR), 3, 2) + '-' + Cast(#ID AS NVARCHAR)
RETURN #result
END
Example Trigger:
ALTER TRIGGER [dbo].[trTblNameSetRefNo] ON [dbo].[TblName]
AFTER INSERT AS BEGIN
UPDATE TblName
SET RefNumber = dbo.GetNextRefNo(i.ID)
FROM TblName
INNER JOIN inserted i on i.ID = TblName.ID
END
Alternatively create another table to hold the current RefNumber seed.

Related

How to find hardcoded values defined in text using MS SQL query without using functions

Requirement: I have a table for storing queries (SQL programs). I need to search and find out in this table such queries which have hardcoded values for a particular column (name) as shown below:
SELECT
*
FROM TABLE1 AC
WHERE
AC.name = 'hardcoded_value1'
UNION
SELECT
*
FROM TABLE2 BC
WHERE BC.name = 'hardcoded_value2'
I have tried and done this using a function and it works fine. But the requirement has a constraint which doesn't allow to make use of any function or stored procedure.
Below is the function definition for reference:-
CREATE OR ALTER FUNCTION [dbo].[GetConstantValue](#QueryID INT)
RETURNS
#Constantvalue TABLE
(
name_ NVARCHAR(2000)
)
AS
BEGIN
Declare #Query NVARCHAR(max) = SELECT code FROM QUERY_TABLE WHERE ID = #QueryID
Declare #StartIndex int = 0,#EndIndex int = 0,#Count int = 0,#ConstStr nvarchar(max) = ''
WHILE #Count <= LEN(#Query)
BEGIN
IF SUBSTRING(#Query,#Count, 1) = CHAR(39)
BEGIN
IF #StartIndex <> 0
BEGIN
SET #ConstStr = #ConstStr + CASE WHEN LEN(#ConstStr)>0 THEN '|' ELSE '' END+ SUBSTRING(#Query,#StartIndex+1,#Count-(#StartIndex+1))
SET #StartIndex = 0
SET #EndIndex = 0
END
ELSE
IF SUBSTRING(#Query,#Count-20, 20) LIKE '%name%[=]%'
SET #StartIndex = #Count
END
SET #Count = #Count + 1
END
INSERT INTO #Constantvalue
SELECT Value FROM string_split(#ConstStr,'|')
RETURN
END
Please suggest me a way to achieve this in the main query itself without making any function calls

Assigning a variable by running script from cell in a table

I have a table for data issues, with the code that identified it, and a separate table with the figures for how many rows it affects, what I want to do is use the script (example code) to update the current number of rows (week1)
I have set up a loop so it will pick up the script from that cell and assign is to a variable. To get an output I would ordinarily just exec the variable, but as I want this to update the cell, I'm trying to get it to alter the week1 detail, but get a syntax problem trying to do anything with the exec apart from just exec it
declare #srow int, #erow int, #example varchar(max)
set #srow = 1
set #erow = (select max(id) from #log)
while #srow <= #erow
BEGIN
set #example = (select ExampleCode from #DQLog where ID = #srow)
update #log
set Week1 = exec (#example)
where id = #srow
set #srow = #srow + 1
END
The #example should be set to the examplecode script, and then the execute gives the result and assigns it to the week1 column, but that's not acceptable in mssql. Is there a way around this?
You cannot use exec to return a value from a select. One way to do this is to use a temp table to hold the value such as:
declare #srow int, #erow int, #example varchar(max)
set #srow = 1
set #erow = (select max(id) from #log)
CREATE TABLE #tmp(ID INT)
while #srow <= #erow
BEGIN
set #example = select ExampleCode from #DQLog where ID = #srow
TRUNCATE TABLE #tmp
INSERT INTO #tmp
EXEC #example
update #log
set Week1 = (SELECT TOP 1 ID from #tmp)
where id = #srow
set #srow = #srow + 1
END
If you had modified your script in the table to have it return a variable such as:
set #example = 'select #ExOut = ExampleCode from #DQLog where ID = ' + CAST(#srow as varchar(5))
You could use sp_executeSql to return that value to a variable instead:
DECLARE #nOut int
EXEC sp_executesql #example, N'#ExOut int OUTPUT', #ExOut = #nOut OUTPUT
Having understood your question better. This is the way to execute your code.
execute sp_executesql #examplecode;

SQL Server : input a string of IDs and Return a string of values

I have a table called tblEventDates with DateID and EventDate columns. I need to provide a stored procedure the param of #DateIDs which is a string of DateIDs separated by a pipe, and return a string of EventDates separated by a comma.
This is what I have tried so far
CREATE PROCEDURE [dbo].[ParseDates]
#DateIDs VARCHAR(100),
#ReturnDates VARCHAR(8000) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #i INT,
#DateID INT,
#D VARCHAR(8000),
#TmpDate VARCHAR(8000)
SET #D = #DateIDs
SET #i = CHARINDEX('|', #D)
WHILE #i > 0
BEGIN
SET #DateID = CONVERT(INT, SUBSTRING(#D, 1, #i - 1))
SELECT #TmpDate = EventDate
FROM tblEventDates
WHERE DateID = #DateID
SET #ReturnDates = CONCAT(#ReturnDates, ', ', #TmpDate)
SET #D = SUBSTRING(#D, #i + 1, 9999)
SET #i = CHARINDEX('|', #D)
CONTINUE
END
IF LEN(#D) > 0
BEGIN
SELECT #TmpDate = EventDate
FROM tblEventDates
WHERE DateID = #DateID
SET #ReturnDates = CONCAT(#ReturnDates, ', ', #TmpDate)
END
END
GO
So if #DateIDs = '65|67|69', #ReturnDates should be '01/15/2019, 01/16/2019, 01/17/2019'.
There's a brute force option as well
Example
Declare #S varchar(max) = '65|67|69'
Set #S='|'+#S+'|'
Select #S = replace(#S,concat('|',DateID,'|'),'|'+convert(varchar(10),EventDate,101)+'|')
From tblEventDates
Select replace(substring(#S,2,len(#S)-2),'|',', ')
Returns
01/15/2019, 01/16/2019, 01/17/2019
Providing you are using SQL Server 2016 or higher, you can leverage the STRING_SPLIT function to turn the delimited #DateIDs string into a table, join that against your date table (which I have called my_date_table with the fields id and date_value), and then concatenate the dates using a trick involving JSON_VALUE and REPLACE (found here):
DROP TABLE IF EXISTS #dates;
SELECT
CONVERT(VARCHAR(10), my_date_table.date_field, 101) AS date_string
INTO #dates
FROM my_date_table
INNER JOIN (
SELECT [value]
FROM STRING_SPLIT(#DateIDs, '|')
) date_ids
ON date_ids.[value] = my_date_table.id
;
SET #returnDates = COALESCE(
JSON_VALUE(
REPLACE(
(
SELECT
_ = date_string
FROM #dates
FOR JSON PATH
)
,'"},{"_":"',', '),'$[0]._'
)
, '');

Return first five numbers form string within UDF

I am working on SQL Server (2005,2008 & 2012)
I wanna extract first five numbers from varchar column via using UDF
Input:
rrr123ddd4567ddd19828www2
123hhhsss124ss18762s
qq12349wsss12376ss
Output:
19828
18762
12349
My Trail is as following:
DECLARE
#myString VARCHAR(1000),
#temp VARCHAR(100),
#position INT,
#ExecuteInsert nvarchar (500),
#FirstChar bit
SET #myString = 'rrr123ddd4567ddd19828www2'
SET #position = 1
SET #FirstChar = 1
WHILE #position <= LEN(#myString)
BEGIN
IF (ISNUMERIC(SUBSTRING(#myString,#position,1))) = 1
BEGIN
SET #temp = isnull(#temp,'') + SUBSTRING(#myString,#position,1)
SET #FirstChar = 1
END
ELSE /* The char is alphabetical */
BEGIN
if (#FirstChar= 1)
BEGIN
SET #temp = isnull(#temp,'') + ','
SET #FirstChar = 0
END
END
SET #position = #position + 1
END
IF (RIGHT(#temp,1) <> ',')
BEGIN
SET #temp = #temp + ','
END
SELECT #temp = REPLACE(','+ #temp + ',',',,','')
SELECT #temp = Replace (#temp,',','''),(''')
Select #temp = '(''' + #temp + ''')'
Create table #temp
(
col1 varchar(100)
)
SET #ExecuteInsert = 'insert into #temp values ' + #temp
Execute sp_executesql #ExecuteInsert
select top 1 col1 from #temp
where LEN(col1) = 5
drop table #temp
-- Output >> 19828
The previous query is working well with string input , but I wanna using this code within UDF to could using it with columns.
if I used the previous query within UDF, the following error is raising:
Cannot access temporary tables from within a function.
EDIT
if I used Table variable , I get the next error:
Only functions and some extended stored procedures can be executed
from within a function.
any help will be greatly appreciated.
CREATE FUNCTION udfTest
(
-- Add the parameters for the function here
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE
#Result int,
#myString VARCHAR(1000),
#temp VARCHAR(100),
#position INT,
#ExecuteInsert nvarchar (500),
#FirstChar bit
SET #myString = 'rrr123ddd4567ddd19828www2'
SET #position = 1
SET #FirstChar = 1
WHILE #position <= LEN(#myString)
BEGIN
IF (ISNUMERIC(SUBSTRING(#myString,#position,1))) = 1
BEGIN
SET #temp = isnull(#temp,'') + SUBSTRING(#myString,#position,1)
SET #FirstChar = 1
END
ELSE /* The char is alphabetical */
BEGIN
if (#FirstChar= 1)
BEGIN
SET #temp = isnull(#temp,'') + ','
SET #FirstChar = 0
END
END
SET #position = #position + 1
END
IF (RIGHT(#temp,1) <> ',')
BEGIN
SET #temp = #temp + ','
END
SELECT #temp = REPLACE(','+ #temp + ',',',,','')
SELECT #temp = Replace (#temp,',','''),(''')
Select #temp = '(''' + #temp + ''')'
Declare #tempTable TABLE
(
col1 varchar(100)
)
insert into #tempTable SELECT #temp
select top 1 #Result=col1 from #tempTable
where LEN(col1) = 5
return #Result
END
GO
Here you are my answer of my question , hope helps others.
The objective is creating UDF function for using it with columns, not only fixed values.
The approach is using SplitString instead of sp_executesql
for splitting a comma separated string and loop it's values in table.
Demo:-
Create table DummyTable
( col1 varchar (100))
go
Insert into DummyTable values ('rrr123ddd4567ddd19828www2')
Insert into DummyTable values ('123hhhsss124ss18762s')
Insert into DummyTable values ('qq12349wsss12376ss')
go
/*
SplitString via Mudassar Khan
http://www.aspsnippets.com/Articles/Split-and-convert-Comma-Separated-Delimited-String-to-Table-in-SQL-Server.aspx
*/
Create FUNCTION SplitString
(
#Input NVARCHAR(MAX),
#Character CHAR(1)
)
RETURNS #Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE #StartIndex INT, #EndIndex INT
SET #StartIndex = 1
IF SUBSTRING(#Input, LEN(#Input) - 1, LEN(#Input)) <> #Character
BEGIN
SET #Input = #Input + #Character
END
WHILE CHARINDEX(#Character, #Input) > 0
BEGIN
SET #EndIndex = CHARINDEX(#Character, #Input)
INSERT INTO #Output(Item)
SELECT SUBSTRING(#Input, #StartIndex, #EndIndex - 1)
SET #Input = SUBSTRING(#Input, #EndIndex + 1, LEN(#Input))
END
RETURN
END
GO
-------------------------------------
-------------------------------------
-------------------------------------
/*
My Own Function
*/
Create FUNCTION udfGetFirstFiveNumbers
(
#myString VARCHAR(1000)
)
RETURNS varchar(100)
AS
BEGIN
DECLARE
#temp VARCHAR(100),
#result Varchar (100),
#position INT,
#ExecuteInsert nvarchar (500),
#FirstChar bit
SET #position = 1
SET #FirstChar = 1
WHILE #position <= LEN(#myString)
BEGIN
IF (ISNUMERIC(SUBSTRING(#myString,#position,1))) = 1
BEGIN
SET #temp = isnull(#temp,'') + SUBSTRING(#myString,#position,1)
SET #FirstChar = 1
END
ELSE /* The char is alphabetical */
BEGIN
if (#FirstChar= 1)
BEGIN
SET #temp = isnull(#temp,'') + ','
SET #FirstChar = 0
END
END
SET #position = #position + 1
END
IF (RIGHT(#temp,1) <> ',')
BEGIN
SET #temp = #temp + ','
END
SELECT #temp = REPLACE(','+ #temp + ',',',,','')
SELECT #result = Item
FROM dbo.SplitString(#temp, ',')
where len(Item) = 5
return #result
END
GO
-- Test
select col1, dbo.udfGetFirstFiveNumbers(col1) as result
from DummyTable
Result:-

Inserting using CSV

I have a table with two fields Name and ID.
Name comes from input parameter #Name and ID comes a CSV #CSVID.
I have a spilt function that return a temp table .
My stored proc is
INSERT INTO dbo.MyTable
(
Name,
ID
)
VALUES
(
(SELECT #Name, id FROM dbo.Split(#CSVID))
)
My split function
ALTER FUNCTION [dbo].[Split] (#InStr VARCHAR(MAX))
RETURNS #TempTab TABLE
(id int not null)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SELECT #SP = PATINDEX('%,%',#INSTR)
SELECT #VALUE = LEFT(#INSTR , #SP - 1)
SELECT #INSTR = STUFF(#INSTR, 1, #SP, '')
INSERT INTO #TempTab(id) VALUES (#VALUE)
END
RETURN
END
So what I want is
say
#Name = 'John Doe'
#CSVID = '1,2'
Then I want the result of insert to be
Name ID
John 1
Jhon 2
I saw so many example but they were all so complicated. I just a simple explanation as to how insert works if the subquery
SELECT * FROM dbo.Split(#CSVID)
returns more than 1 value.
Thanks
INSERT INTO dbo.MyTable(Name, ID)
SELECT #Name,
id
FROM dbo.Split(#CSVID)

Resources