multi-statement table-valued function problem - sql-server-2008

I have problem with multi-statement table-valued function . Whats wrong whit this code
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
CREATE FUNCTION FN_BFS_CFS_BALANCE_VIW
(
#param1 VARCHAR
)
RETURNS #table TABLE
(
DAT VARCHAR
, COD_ACN_KOL VARCHAR
, DES_ACN_KOL VARCHAR
, COD_ACN_MOIN VARCHAR
, DES_ACN_MOIN VARCHAR
, COD_ACN_TAFSIL VARCHAR
, DES_ACN_TAFSIL VARCHAR
, COD_FAREI1 VARCHAR
, DES_FAREI1 VARCHAR
, COD_FAREI2 VARCHAR
, DES_FAREI2 VARCHAR
, TYP_FRGN INT
, DES_FRGN VARCHAR
, TYP_DPN INT
, DES_DPN VARCHAR
, FLG_129 INT
, DES_129 VARCHAR
, AMN_DBT INT
, AMN_CRD INT
, COD_MONEY VARCHAR
, DES_MONEY VARCHAR
, AMN_DBT_FRGN INT
, AMN_CRD_FRGN INT
)
AS
BEGIN
EXEC (' call tpout.set_context(''PARAM1'',' + '' + #param1 + '' + ' AT [LS_New2CFS]');
SELECT DAT
, COD_ACN_KOL
, DES_ACN_KOL
, COD_ACN_MOIN
, DES_ACN_MOIN
, COD_ACN_TAFSIL
, DES_ACN_TAFSIL
, COD_FAREI1
, DES_FAREI1
, COD_FAREI2
, DES_FAREI2
, TYP_FRGN
, DES_FRGN
, TYP_DPN
, DES_DPN
, FLG_129
, DES_129
, AMN_DBT
, AMN_CRD
, COD_MONEY
, DES_MONEY
, AMN_DBT_FRGN
, AMN_CRD_FRGN
FROM LS_New2CFS..TPOUT.BFS_CFS_BALANCE_VIW;
RETURN;
END;
when I execute this code this error showing up.
PS: [LS_New2CFS] is Oracle link server. [BFS_CFS_BALANCE_VIW] is a view in the Oracle database.
Msg 444, Level 16, State 2, Procedure FN_BFS_CFS_BALANCE_VIW, Line 37 [Batch Start Line 5]
Invalid use of a side-effecting operator 'EXECUTE STRING' within a function.
Msg 443, Level 16, State 14, Procedure FN_BFS_CFS_BALANCE_VIW, Line 33 [Batch Start Line 5]
Select statements included within a function cannot return data to a client.
Thanks to you

Related

Looking for a function to using IF ELSE condition in MYSQL

I have a table data with combination of Integer and strings. Like this
CREATE TABLE EMPLOYEE (
empId INTEGER,
name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT NOT NULL
);
INSERT INTO EMPLOYEE VALUES (12345, 'Clark Duff', 'Sales#yahoo.com',9001234567);
INSERT INTO EMPLOYEE VALUES (22245, 'Dave Johnson', 'Accounting#gmail.com',9000123456);
INSERT INTO EMPLOYEE VALUES (55456, 'Ava evelene', 'Sales_Marketing#gmail.com',9000012345);
But I'm looking for a common function in MYSQL to check the condition whether it is a special character or integer or string. Below syntax will mask the data:
If it is an integer then this condition needs to execute:
concat(SUBSTRING(phone,1,3) , '*****' , SUBSTRING(phone,7,4)) phone
If it is a special character (#) then this condition needs to execute:
CONCAT(LEFT(UUID(), 8), '#', SUBSTRING_INDEX(`Mail`, '#', -1)) as Mail
ELSE string then other script
seem you are looking for case statement applied to like condition for # char or cast to usingned for chekc a valid integer number
select case
when email like '%#%' then CONCAT(LEFT(UUID(), 8), '#', SUBSTRING_INDEX(`Mail`, '#', -1))
when cast(phone AS UNSIGNED) != 0 THEN concat(SUBSTRING(phone,1,3) , '*****' , SUBSTRING(phone,7,4))
else 'not managed'
end
One UDF to mask them.
CREATE FUNCTION fnMaskUserInfo (
input TEXT
)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF input LIKE '%_#_%._%'
THEN
RETURN CONCAT(RIGHT(SHA1(input),8),'#',SUBSTRING_INDEX(input,'#',-1));
END IF;
IF input REGEXP '^[0-9]{7,10}$'
THEN
RETURN CONCAT(SUBSTRING(input,1,3),'*****',SUBSTRING(input,7,4));
END IF;
RETURN input;
END
SELECT empId
, fnMaskUserInfo(Name) AS Name
, fnMaskUserInfo(email) AS email
, fnMaskUserInfo(phone) AS phone
FROM EMPLOYEE
empId
Name
email
phone
12345
Clark Duff
1beb980f#yahoo.com
900*****4567
22245
Dave Johnson
1dadfff9#gmail.com
900*****3456
55456
Ava evelene
3a0768e7#gmail.com
900*****2345
Demo on db<>fiddle here
Here is an example stored function. Is this what you mean?
CREATE FUNCTION `someFunc`(
input VARCHAR(255)
)
RETURNS VARCHAR(255)
NOT DETERMINISTIC
BEGIN
IF input LIKE '%#%' THEN
RETURN CONCAT(LEFT(UUID(), 8), '#', SUBSTRING_INDEX(input, '#', -1));
ELSEIF input REGEXP '^[0-9 ]{7,10}$' THEN
RETURN CONCAT(SUBSTRING(input, 1, 3) , '*****' , SUBSTRING(input, 7, 4));
ELSE
RETURN input;
END IF;
END
You may want to use LEFT(MD5(input), 8) instead of LEFT(UUID(), 8) so that the function can be declared as deterministic.
CREATE FUNCTION `someFunc`(
input VARCHAR(255)
)
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
IF input LIKE '%#%' THEN
RETURN CONCAT(LEFT(MD5(input), 8), '#', SUBSTRING_INDEX(input, '#', -1));
ELSEIF input REGEXP '^[0-9 ]{7,10}$' THEN
RETURN CONCAT(SUBSTRING(input, 1, 3) , '*****' , SUBSTRING(input, 7, 4));
ELSE
RETURN input;
END IF;
END
WITH `EMPLOYEE` (`empId`, `name`, `email`, `phone`) AS (
SELECT 12345, 'Clark Duff', 'Sales#yahoo.com',9001234567 UNION
SELECT 22245, 'Dave Johnson', 'Accounting#gmail.com',9000123456 UNION
SELECT 55456, 'Ava evelene', 'Sales_Marketing#gmail.com',9000012345
)
SELECT
empId,
someFunc(`name`) AS `name`,
someFunc(`email`) AS `email`,
someFunc(`phone`) AS `phone`
FROM `EMPLOYEE`;
# empId
name
email
phone
12345
Clark Duff
b3282336#yahoo.com
900*****4567
22245
Dave Johnson
47c44ab0#gmail.com
900*****3456
55456
Ava evelene
ab3fee5c#gmail.com
900*****2345

MySQL Query (Sub Queries + Composed Functions + JOIN operations) takes too long to run

How can I revise the following query with subqueries composed of functions and join queries in functions. I want to append extra values to my main query that relies on main tables and two primary joins (risks, users) on several occasions.
Creating a MCVRE (Minimal Complete Verifiable Reproduceable Example) proved to be somewhat challenging because of request sent to SQL Fiddle has too many rows (too many text characters) After removing nearly all rows on main two tables ( users, risks ) I ended up with a running query.
The Fiddle (http://www.sqlfiddle.com/#!9/1d52a0/17) create functions and insertion of data commands have reduced rows from actual example on my local pc due to character count of 8000 being exceeded for request payload for SQLFiddle to understand.
Actual table has about 100 rows for risks, and 20 or so rows for users and takes about 3 seconds to run
What can I do to speed up query, via staving desired function results in table, or by revision, index insertion, movement of joins to outer main query, or even using stored procedure, or rewriting query structure, to reduce execution time to possibly half the time or less optimistically. SQL fiddle does not take all rows needed so I pasted a very limited subset, Even SQLFiddle query (see total select query below) does not run, due to Stack Overflow (pun partially intended).
http://www.sqlfiddle.com/#!9/1d52a0/17
Base Queries that do run on the fiddle (see fiddle)
select * from users;
select * from risks;
select * from riskevents;
select * from riskmatrixthresholds;
select * from risklevels;
#significantly minimized result set but still query does not run due to stack overflow issue on sql fiddle - see fiddle result (on bottom most portion of fiddle query output)
SELECT r.RiskID,
r.CreatorID,
r.OwnerID,
r.ApproverID,
r.RiskTitle,
r.RiskStatement,
r.ClosureCriteria,
r.RiskState,
r.Context AS 'Context',
GetRiskUserLastOrFirstName(GetRiskUserID('Creator', r.RiskID,0),r.RiskID, 'Last','') AS 'creator.lastname',
GetRiskUserLastOrFirstName(GetRiskUserID('Creator', r.RiskID,0),r.RiskID, 'First','') AS 'creator.firstname',
GetRiskUserLastOrFirstName(GetRiskUserID('Owner', r.RiskID,0),r.RiskID, 'Last','') AS 'owner.lastname',
GetRiskUserLastOrFirstName(GetRiskUserID('Owner', r.RiskID,0),r.RiskID, 'First','') AS 'owner.firstname',
GetRiskUserLastOrFirstName(GetRiskUserID('Approver',r.RiskID,0),r.RiskID, 'Last','') AS 'approver.lastname',
GetRiskUserLastOrFirstName(GetRiskUserID('Approver',r.RiskID,0),r.RiskID, 'First','') AS 'approver.firstname',
r.Likelihood AS 'OriginalLikelihood',
r.Technical AS 'OriginalTechnical',
r.Schedule AS 'OriginalSchedule',
r.Cost AS 'OriginalCost',
GREATEST(r.Technical, r.Schedule, r.Cost) AS 'OriginalConsequence',
RiskValue(r.Likelihood, GREATEST(r.Technical, r.Schedule, r.Cost),0) AS 'OriginalValue',
RiskLevel(RiskValue(r.Likelihood, GREATEST(r.Technical, r.Schedule, r.Cost),0),'') AS 'OriginalLevel',
LatestEventDate(r.RiskID, r.AssessmentDate,'') AS 'LatestEventDate',
r.AssessmentDate AS 'AssessmentDate',
(SELECT CurrentLikelihood(r.RiskID,0)) AS 'CurrentLikelihood',
(SELECT CurrentConsequence(r.RiskID,0)) AS 'CurrentConsequence',
(SELECT CurrentRiskValue(r.RiskID,0)) AS 'CurrentValue',
(SELECT RiskLevel(CurrentRiskValue(r.RiskID,0),'')) AS 'CurrentLevel'
FROM risks r;
Create Function Script
CREATE TABLE `riskevents` (
`ID` int NOT NULL AUTO_INCREMENT,
`EventID` int ,
`RiskID` int ,
`EventTitle` text,
`EventStatus` varchar(10) ,
`EventOwnerID` int ,
`ActualDate` date ,
`ScheduleDate` date ,
`BaselineDate` date ,
`ActualLikelihood` int ,
`ActualTechnical` int ,
`ActualSchedule` int ,
`ActualCost` int ,
`ScheduledLikelihood` int ,
`ScheduledTechnical` int ,
`ScheduledSchedule` int ,
`ScheduledCost` int ,
`BaselineLikelihood` int ,
`BaselineTechnical` int ,
`BaselineSchedule` int ,
`BaselineCost` int ,
PRIMARY KEY (`ID`)
)
CREATE TABLE `risklevels` (
`ID` int NOT NULL AUTO_INCREMENT,
`RiskLevelID` int ,
`RiskMaximum` float ,
`RiskHigh` float ,
`RiskMedium` float ,
`RiskMinimum` float ,
PRIMARY KEY (`ID`)
)
CREATE TABLE `riskmatrixthresholds` (
`ID` int NOT NULL AUTO_INCREMENT,
`CellID` int ,
`Likelihood` int ,
`Consequence` int ,
`Level` decimal(2,2) ,
PRIMARY KEY (`ID`)
)
CREATE TABLE `risks` (
`ID` int NOT NULL AUTO_INCREMENT,
`RiskState` varchar(10) ,
`RiskID` int ,
`RiskTitle` text CHARACTER SET latin1,
`RiskStatement` text CHARACTER SET latin1,
`ApproverID` int ,
`OwnerID` int ,
`CreatorID` int ,
`Likelihood` int ,
`Technical` int ,
`Schedule` int ,
`Cost` int ,
`ClosureCriteria` text CHARACTER SET latin1,
`CategoryID` int ,
`AssessmentDate` date ,
`CompletionDate` date ,
`ClosureDate` date ,
`Context` text,
PRIMARY KEY (`ID`),
UNIQUE KEY `risk_index` (`RiskID`)
)
CREATE TABLE `users` (
`ID` int NOT NULL AUTO_INCREMENT,
`UserID` int NOT NULL,
`LastName` char(25) ,
`FirstName` char(15) ,
`Title` char(20) ,
`Email` varchar(30) ,
`Phone` char(12) ,
`Extension` char(4) ,
`Department` char(25) ,
PRIMARY KEY (`ID`),
UNIQUE KEY `user_index` (`UserID`),
KEY `SURROGATE` (`UserID`)
)
insert into `riskevents`(`ID`,`EventID`,`RiskID`,`EventTitle`,`EventStatus`,`EventOwnerID`,`ActualDate`,`ScheduleDate`,`BaselineDate`,`ActualLikelihood`,`ActualTechnical`,`ActualSchedule`,`ActualCost`,`ScheduledLikelihood`,`ScheduledTechnical`,`ScheduledSchedule`,`ScheduledCost`,`BaselineLikelihood`,`BaselineTechnical`,`BaselineSchedule`,`BaselineCost`) values
(171,0,1,'Risk','Complete',5,'2019-06-14',NULL,'2019-06-14',5,2,2,5,NULL,NULL,NULL,NULL,5,2,2,5),
(184,0,10,'Risk','Complete',21,'2019-10-07',NULL,'2019-10-07',5,4,5,4,NULL,NULL,NULL,NULL,5,4,5,4));
insert into `risklevels`(`ID`,`RiskLevelID`,`RiskMaximum`,`RiskHigh`,`RiskMedium`,`RiskMinimum`) values
(1,1,1,0.55,0.3,0);
insert into `riskmatrixthresholds`(`ID`,`CellID`,`Likelihood`,`Consequence`,`Level`) values
(1,1,1,1,0.09),
(2,2,1,2,0.12),
(3,3,1,3,0.16),
(4,4,1,4,0.19),
(5,5,1,5,0.23),
(6,6,2,1,0.12),
(7,7,2,2,0.19),
(8,8,2,3,0.27),
(9,9,2,4,0.34),
(10,10,2,5,0.41),
(11,11,3,1,0.16),
(12,12,3,2,0.27),
(13,13,3,3,0.37),
(14,14,3,4,0.48),
(15,15,3,5,0.59),
(16,16,4,1,0.19),
(17,17,4,2,0.34),
(18,18,4,3,0.48),
(19,19,4,4,0.63),
(20,20,4,5,0.77),
(21,21,5,1,0.23),
(22,22,5,2,0.41),
(23,23,5,3,0.59),
(24,24,5,4,0.77),
(25,25,5,5,0.95);
insert into `risks`(`ID`,`RiskState`,`RiskID`,`RiskTitle`,`RiskStatement`,`ApproverID`,`OwnerID`,`CreatorID`,`Likelihood`,`Technical`,`Schedule`,`Cost`,`ClosureCriteria`,`CategoryID`,`AssessmentDate`,`CompletionDate`,`ClosureDate`,`Context`) values
(1,'Completed',1,'t','t',1,5,1,5,2,2,5,'t',NULL,'2019-06-14','2020-09-26',NULL,'t'),
(2,'Completed',2,'t','t',2,1,1,5,3,4,2,'test',NULL,'2019-05-14',NULL,NULL,'t'),
insert into `users`(`ID`,`UserID`,`LastName`,`FirstName`,`Title`,`Email`,`Phone`,`Extension`,`Department`) values
(1,1,'Admin','','Admin','a#yz.com','17890','1234',''),
(2,2,'Last','First','Engineer','a#yz.com','123890','1234','Supplier');
CREATE FUNCTION Consequence(technical int, sched int, cost int, consequence int) RETURNS int
BEGIN
select GREATEST(technical, sched, cost) into consequence;
return consequence;
END;
CREATE FUNCTION CurrentRiskEventID(riskidentifier int, eid int) RETURNS int
BEGIN
select MAX(e.EventID) into eid
FROM riskevents e
WHERE e.eventstatus not in('Open')
AND e.riskid = riskidentifier;
return riskeventid;
END;
CREATE FUNCTION CurrentConsequence(riskidentifier int, currentconsequence int) RETURNS int
BEGIN
SELECT coalesce(
(SELECT GREATEST(actualtechnical, actualschedule, actualcost)
FROM riskevents
WHERE id = CurrentRiskEventID(riskidentifier, 0)
and actualtechnical is not null
ANDactualschedule is not null
andactualschedule is not null),
(SELECT greatest(technical, schedule, cost)
from risks
Where riskid = riskidentifier)
) into currentconsequence;
return currentconsequence;
END;
CREATE FUNCTION CurrentLikelihood(riskidentifier int, currentlikelihood int) RETURNS int
BEGIN
SELECT coalesce(
(SELECT actuallikelihood
FROM riskevents
WHERE id = CurrentRiskEventID(riskidentifier, 0)),
(SELECT r.likelihood
FROM risks r
WHERE r.riskid = riskidentifier)) into currentlikelihood;
return currentlikelihood;
END;
CREATE FUNCTION CurrentRiskLevel(riskidentifier int, currentrisklevel int) RETURNS int
BEGIN
select RiskLevel(CurrentRiskValue(riskidentifier, 0), '') into currentrisklevel;
return currentrisklevel;
END;
CREATE FUNCTION CurrentRiskValue(riskidentifier int, currentriskvalue int) RETURNS int
BEGIN
SELECT RiskValue(CurrentLikelihood(riskidentifier, 0), CurrentConsequence(riskidentifier, 0), 0) into currentriskvalue;
return currentriskvalue;
END;
CREATE FUNCTION GetRiskUserID(riskusertype VARCHAR(25), riskidentifier int, riskuserid int) RETURNS int
BEGIN
SELECT COALESCE(userres.userid, 0) into riskuserid FROM
(
SELECT r.creatorid, r.ownerid, r.approverid, u.userid
FROM risks r, users u
WHERE r.riskid = (select riskidentifier) and
(
((select riskusertype) = 'Creator' AND u.userid = r.creatorid) OR
((select riskusertype) = 'Approver' AND u.userid = r.approverid) OR
((select riskusertype) = 'Owner' AND u.userid = r.ownerid)
)
) userres;
RETURN riskuserid;
END;
CREATE FUNCTION GetRiskUserLastOrFirstName(riskuserid int, riskid int, whichname char(25), firstorlastname char(25)) RETURNS char(25) CHARSET utf8 COLLATE utf8_unicode_ci
BEGIN
SELECT (case
when whichname = 'Last' then u.LastName
WHEN whichname = 'First' THEN u.FirstName
end)
into firstorlastname
FROM users u,risks r
WHERE u.UserID = riskuserid
AND r.RiskID = riskid;
return firstorlastname;
END;
CREATE FUNCTION LatestEventDate(riskidentifier int, riskassessmentdate date, latestdate date) RETURNS date
BEGIN
SELECT COALESCE(
(SELECT ActualDate FROM riskevents evt WHERE evt.eventid = CurrentRiskEventID(riskidentifier, 0) and evt.riskid = riskidentifier),
(SELECT riskassessmentdate)) into latestdate;
return latestdate;
END;
CREATE FUNCTION RiskLevel(riskvalue int, risklevel varchar(4)) RETURNS varchar(4)
begin
SELECT
CASE
WHEN riskvalue >= levels.riskhigh*100 THEN 'High'
WHEN riskvalue >= levels.riskmedium*100 THEN 'Med'
ELSE 'Low'
ENd as cat into risklevel
FROM risklevels levels;
return risklevel;
END;
CREATE FUNCTION RiskValue(likelihood int, consequence int, riskvalue int) RETURNS int
BEGIN
SELECT m.level*100 INTO riskvalue FROM riskmatrixthresholds m WHERE m.likelihood = likelihood AND m.consequence = consequence;
RETURN riskvalue;
END;
http://www.sqlfiddle.com/#!9/1d52a0/17
Note: SQL is a declarative language, not a procedural language. You tell it what you want, not how to get it. Your use of functions and so forth is procedural.
How can you make this application faster?
First, use the latest version of MySQL (8+, or MariaDB 10.4+). Later versions get faster.
Second, you have stated a requirement to use "subqueries composed of functions". That means you probably can't do much about the performance.
Why not? The subqueries buried in functions are so-called dependent subqueries. Those don't perform well. And because they're buried MySQL's query planner can't do anything useful to optimize them.
Refactoring your query to avoid using functions with SELECT operations will give the query planner visibility into your overall query. That will give it a chance to optimize things. You might replace them with views.
And don't use SELECT tablea, tableb syntax. That's been obsolete since 1992. Use
SELECT tablea JOIN tableb ON tablea.joincolumn = tableb.joincolumn.
I'd offer you more advice but I can't figure out your intent.
Application of the following changes in CurrentLikelihood() and CurrentConsequence() reduced total query execution time to exec 0.070 sec, total 0.082 sec.
Old Current Likelihood Query (producing slow and incorrect output)
SELECT coalesce(
(SELECT actuallikelihood
FROM riskevents
WHERE id = CurrentRiskEventID(riskidentifier, 0)),
(SELECT r.likelihood
FROM risks r
WHERE r.riskid = riskidentifier)) into currentlikelihood;
return currentlikelihood;
Working CurrentLikelihood Query
SELECT actuallikelihood INTO currentlikelihood
FROM riskevents
WHERE eventid = CurrentRiskEventID(riskidentifier)
AND riskid = riskidentifier;
Old CurrentConsequence Query (producing slow and incorrect output)
SELECT coalesce(
(SELECT GREATEST(actualtechnical, actualschedule, actualcost)
FROM riskevents
WHERE id = CurrentRiskEventID(riskidentifier, 0)
and actualtechnical is not null
and actualschedule is not null),
(SELECT greatest(technical, schedule, cost)
from risks
Where riskid = riskidentifier)
) into currentconsequence;
Working CurrentConsequence Query
SELECT GREATEST(actualtechnical, actualschedule, actualcost) INTO currentconsequence
FROM riskevents
WHERE eventid = CurrentRiskEventID(riskidentifier)
AND riskid = riskidentifier;
Old CurrentRiskEventID() Query
select MAX(e.EventID) into currentriskeventid
FROM riskevents e
WHERE e.eventstatus not in('Open')
AND e.riskid = riskidentifier;
Modified GetRiskEventID() function
SELECT MAX(e.EventID) INTO currentriskeventid
FROM riskevents e
WHERE e.riskid = riskidentifier AND
(e.eventstatus != 'Open'
OR
(e.EventID = 0 AND e.eventstatus = 'Open'));

SQL Server 2008 OPENQUERY inside sp_executesql. Getting error incorrect syntax?

Code:
DECLARE #resultLast int, #siparisID nvarchar(21)
SET #siparisID = 2487
EXEC sp_executesql N'select * from OPENQUERY([MYSERVER],''Select ( [MYDB].[dbo].[FN_SIPARIS_YUKLEME_TUTARI](#siparisID , 20 ,
[MYDB].[dbo].[FN_DATE_CONVERT_TO_DATE]( GETDATE()) , ''''BUY''''))'' )', #siparisID,
N'#resultLast int output', #resultLast output;
I am trying to fetch data from linked server function. Also i need to send #siparisID parameter. I am getting incorrect syntax error. Help please...
Error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '2487'.
You've defined #siparisID nvarchar(21) - but you're setting it to a numerical datatype.
So either define it as a numeric datatype like int or decimal(p,s):
DECLARE #siparisID INT
DECLARE #siparisID BIGINT
DECLARE #siparisID DECIMAL(16,2)
or else change the initialisation to
SET #siparisID = N'2487';

Table type parameter in a stored procedure cause operand type clash error

I want to give an array of identifiers as argument to a stored procedure.
The stored procedure looks like :
ALTER PROCEDURE [dbo].[SearchPerson]
#personType INT = NULL,
#city NVARCHAR(64) = NULL,
#siteIds IntegerList READONLY,
-- some other params...
AS
SELECT
-- some fields...
FROM dbo.PersonView AS pv
WHERE
(
(#personType IS NULL OR pv.PersonType = #personType) AND
(#city IS NULL OR pv.City LIKE '%' + #city + '%') AND
(pv.SiteId in (SELECT si.Value FROM #siteIds AS si)) AND
-- some other params filter...
)
The user table type looks like :
CREATE TYPE [dbo].[IntegerList] AS TABLE(
[Value] [int] NULL
)
When I call the stored procedure from a script in SSMS (I originally have the same problem calling it from .NET code) :
DECLARE #siteIds AS IntegerList,
#personType AS INT = 1
INSERT INTO #siteIds VALUES (1)
EXEC [dbo].[SearchPerson] #personType, #siteIds
I got the error :
Operand type clash: int is incompatible with IntegerList
I found the answer : it was the order of the table type parameter that caused the error !
The table type parameter must be the first in the stored procedure parameters AND ALSO in the arguments passed to the stored procedure call !
The stored procedure :
ALTER PROCEDURE [dbo].[SearchPerson]
#siteIds IntegerList READONLY, -- THIS PARAMETER HAS TO BE THE FIRST !
#personType INT = NULL,
#city NVARCHAR(64) = NULL,
-- some other params...
AS
SELECT
-- some fields...
FROM dbo.PersonView AS pv
WHERE
(
(#personType IS NULL OR pv.PersonType = #personType) AND
(#city IS NULL OR pv.City LIKE '%' + #city + '%') AND
(pv.SiteId in (SELECT si.Value FROM #siteIds AS si)) AND
-- some other params filter...
)
And the call :
DECLARE #siteIds AS IntegerList,
#personType AS INT = 1
INSERT INTO #siteIds VALUES (1)
EXEC [dbo].[SearchPerson] #siteIds, #personType -- PUT #siteIds FIRST !
A sql server bug or am I missing something ?
DECLARE #ErrMsg varchar(1000)
DECLARE #ServiceDates ServiceDatesType
INSERT #ServiceDates (indexId,unitOfDay,dayOfMonth,dateOfMonth)
VALUES
(0,1,11,'9/11/2016 12:00:00 AM'),
(1,1,12,'9/12/2016 12:00:00 AM'),
(2,1,13,'9/13/2016 12:00:00 AM')
EXEC [usp_SaveValidate] 427,4,12,9,2016,#ErrMsg output,#ServiceDates
PRINT #ErrMsg
*/
ALTER PROCEDURE [dbo].[usp_SaveValidate] (
#EpisodeNo INT
,#ProviderId INT
,#ServiceId INT
,#Month INT
,#Year INT
,#ErrorMessage VARCHAR(1000) OUTPUT
,#ServiceDates ServiceDatesType ReadOnly
)
AS
BEGIN
-- Code Here
END
SQL SERVER 2012 - location of table type parameter does not matter, you have to make sure sequence while passing data, you can check above code which is working fine where table type parameter is at the last.

How to convert a Tsql scalar function into a table function?

I am using SSMS 2008 and I have the following scalar function to take a text string and remove all metatags from Microsoft Word. The tags are enclosed in "<...>" and there can be any number of tags / record in one column.
I created this scalar function to update each row in this column.
create function dbo.ufn_StripHTML
( #Input varchar(max),
#Delimiter char(1)
)
returns varchar(max)
as
begin
declare #Output varchar(max)
select #Input = replace(replace(#input, '<', #Delimiter), '>', #Delimiter)
select #Output = isnull(#Output, '') + s
from ( select row_number() over (order by n.id asc) [i],
substring(#Delimiter + #Input + #Delimiter, n.id + 1, charindex(#Delimiter, #Delimiter + #Input + #Delimiter, n.id + 1) - n.id - 1) [s]
from [evolv_cs].[dbo].[progress_note] n
where n.id = charindex(#Delimiter, #Delimiter + #Input + #Delimiter, n.id) and
n.id <= len(#Delimiter + #Input)
) d
where i % 2 = 1
return #Output
end
This scalar function would work if [progress_note] had an "id" int column. But it does not and I cannot modify this table either, by adding an int column. So the problem is that I am trying to use this function on a temp table.
So I tried creating a view based on this table and then adding a PK int column to it. Because when I tried to create the view with this additional PK int column ("id"), it gave me an error:
Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'identity'.
But ALTER VIEW does not support adding columns. Is there another way to do this? Here is my original temp table I am trying to modify:
select [progress_note].[note_text], [progress_note].[event_log_id]
INTO #TEMP_PN
from [evolv_cs].[dbo].[progress_note]
group by [progress_note].[event_log_id], [progress_note].[note_text]
[note_text] is varchar(max) and event_log_id is uniqueidentifier. So [note_text] contains a bunch of "<" and ">" chars. How can I modify this function to make it a table function?
Of course, if I try to replace [progress_note] table with #TEMP_PN in this function, it will error because it won't recognize it. So how can I modify this function for my case?
Meanwhile, I developed a table function which accepts and outputs a table parameter. It does not error, but it does not return the parsed data I was hoping for either. What is missing?
CREATE TYPE dbo.MyTableType AS TABLE
(
col1 int identity(1,1) NOT NULL,
col2 varchar(max) NULL
)
GO
CREATE TABLE [dbo].[MyTable] (
[col1] [int] identity(1,1) NOT NULL,
[col2] [varchar](max) NULL
)
GO
create PROC usp_AddRowsToMyTable #MyTableParam MyTableType READONLY, #Delimiter varchar(30)
as
INSERT INTO MyTable([col2])
SELECT [col2]
FROM #MyTableParam
--update MyTable
--set col2 = replace(replace(MyTable.col2, '<', #Delimiter), '>', #Delimiter)
select s, i, t
from(
select MyTableInput.col1 [i],
replace(replace(MyTable.col2, '<', #Delimiter), '>', #Delimiter) as t,
substring(#Delimiter + MyTableInput.col2 + #Delimiter, MyTable.col1 + 1,
charindex(#Delimiter, #Delimiter + MyTableInput.col2 + #Delimiter, MyTable.col1 + 1) - MyTable.col1 - 1) [s]
from MyTable
inner join MyTable as MyTableInput on MyTable.col1 = MyTableInput.col1
where MyTable.col1 = CHARINDEX(#Delimiter, #Delimiter + MyTableInput.col2 + #Delimiter, MyTable.col1)
and MyTable.col1 <= LEN(#Delimiter + MyTableInput.col2)
) d
DECLARE #MyTable MyTableType
INSERT INTO #MyTable(col2)
VALUES ('<h><dsf>2000<h><dsf>'),
('<sd><dsf>2001'),
('2002<vnv><dsf>'),
('<gsd><dsf>2003<h><dsf>'),
('<eefs><dsf><h><dsf>2004<dfgd><dsf>')
EXEC dbo.usp_AddRowsToMyTable #MyTableParam = #MyTable, #Delimiter = '|'
Not sure I'm understanding your question, but here is how you would modify your function so that it returns a table (it's called a Table-Valued Function):
create function dbo.ufn_StripHTML
( #Input varchar(max),
#Delimiter char(1)
)
returns #retYourNewTable table
(
id int primary key clustered not null,
yoursecond column varchar(100) null,
....
)
as
....
Is this what you're looking for?