First time question but thought I would see if anyone has a clear idea how to do this. Thank you.
I have a view that contains information about procedures in a hospital. Usual name and date fields and then there is a field that contains all the associated procedures that are concatenated into a single field and pipe delimited. Procedure codes are usually 4 digits and represent each individual procedure completed.
E.g. Name, DateOfBirth,|W822|W833|Z846|Z942|||||||||||||||||||||||||||||||||||||
Procedure codes starting W833,W834,W835 should always be followed by a site code (where on the body the proc was carried out) Z00-Z93 or Z95-Z99
However, it may have a Y code in between (Y282,...) the W and the Z code
There may also be a Z94* following the Y* code
How could you construct a query to find incorrect procedures.
I assume the best way would be a rather convoluted CASE statement. Anyone have any other suggestions?
Thank you.
If you just want to get rows where the procedure codes are in the wrong order you could use a function to validate your procedure codes:
CREATE FUNCTION fn_ValidateOrder ( #InputString NVARCHAR(MAX), #Delimiter NCHAR(1))
RETURNS BIT
AS
BEGIN
DECLARE #Valid BIT = 0, #Errors INT
/* Table to hold splitt string */
DECLARE #T TABLE (id INT IDENTITY(1,1),Val NVARCHAR(128) NULL)
INSERT INTO #T
SELECT value FROM STRING_SPLIT(#InputString,#Delimiter) WHERE value != ''
/* Validated order */
;WITH rn AS (
SELECT
ROW_NUMBER() OVER (ORDER BY val) AS rn
,id,
val
FROM #T
)
/* Count errors */
SELECT #Errors=SUM(CASE WHEN id IS NOT NULL THEN 1 ELSE 0 END) FROM rn WHERE id!=rn
/* Set return value */
IF #Errors>0
SET #Valid=0
ELSE
SET #Valid=1
RETURN #Valid
END
I used the following test data to validate:
/* Test Data*/
DECLARE #data TABLE (DateOfBirth VARCHAR(6),ProcedureCode VARCHAR(MAX))
INSERT INTO #data (DateOfBirth,ProcedureCode)
VALUES ('010176','|W822|W833|Z846|Z942|||||||||||||||||||||||||||||||||||||'),
('120176','|W822|Z282|W833|Z846|Z942|||||||||||||||||||||||||||||||||||||')
/* Validated test data */
SELECT
DateOfBirth,
dbo.fn_ValidateOrder(ProcedureCode,'|') AS ValidOrder,
ProcedureCode
FROM #data
WHERE dbo.fn_ValidateOrder(ProcedureCode,'|') = 0
Note that this will only work on SQL 2016 and newer, as the function use SPLIT_STRING.
Related
I wish to know whether it's feasible to have a TSQL stored procedure return both a result set and the output parameter like so.
create procedure uspReadMyXmlInformation(#myXmlDoc xml, #myProductNum varchar(18) output) as
begin
set nocount on;
declare #myXmlContent table(MyOrderId varchar(12) not null
,CreatedAt datetime not null)
insert into #myXmlContent
select x.c.value('MyOrderID[1]', 'varchar(12)')
x.c.value('CreatedAt[1]', 'datetime')
from #myXmlDoc.nodes('MyRootNodeName/MyChildNodeName') x(c)
set #myProductNum='MyProductNum'
select *
from #myXmlContent
return;
end
So, what happens here is that I can either obtain the result set, when I remove the output parameter, or I obtain the output parameter and the result set is always empty (0=count(*)).
Is there anyway I can obtain both with the same stored procedure or I'd better split them?
I think it's doable from this post in Oracle. I'd like to achieve the same in SQL Server, although constrained to the 2008 version.
Oracle stored procedure: return both result set and out parameters
What I like from doing it using the same SP is that both the result set and the output parameter represent information I read from the XML document. So, the name of the SP says it all, somehow.
EDIT
As some think it might be a duplicate of:
Possible to return an out parameter with a DataReader
I don't think it is as answers there are related as to how the DataReader behaves more than how it could be achieved with TSQL.
The fact is that I get the the value from the output parameter, but I don't get it from the result set at all, it's always returning null.
So, I'm on a SQL Server only project and I'd need that. Otherwise, I'll split it in two, if I can't achieve it in a timely fashion.
Here's how it's used:
declare #xmlInformationData table(MyOrderId varchar(12) not null
,CreatedAt datetime not null)
insert into #xmlInformationData
execute uspReadMyXmlInformation #myXmlDoc, #myProductNum output
while 0<(select count(*) from #xmlInformationData)
begin
-- This will never be executed because I have no rows in #xmlInformationData
-- And yet, before the loop, I have my out param value!
end
The following is a trivial demonstration of using both an output parameter and result set. Try running it a few times and the results should vary.
create procedure Arthur( #TheAnswer as Int Output ) as
begin
-- Set the value of the output parameter.
set #TheAnswer = 42;
-- Generate a single row most of the time.
select GetDate() as NextVogonPoetryReading
where DatePart( millisecond, GetDate() ) < 750;
end;
go 1
-- Declare the variables for the test.
declare #HHGTTU as Table ( HHGTTUId Int Identity, NextVogonPoetryReading DateTime );
declare #SixTimesNine as Int;
-- Execute the SP once so that the while loop might.
insert into #HHGTTU ( NextVogonPoetryReading )
execute Arthur #TheAnswer = #SixTimesNine Output;
-- See what happens.
while exists ( select Pi() from #HHGTTU )
begin
-- See where we are at.
select #SixTimesNine as SixTimesNine, Max( HHGTTUId ) as MaxHHGTTUId, Max( NextVogonPoetryReading ) as MaxNextVogonPoetryReading
from #HHGTTU;
-- Reset.
delete from #HHGTTU;
set #SixTimesNine = 54;
select #SixTimesNine as SixTimesNineAfterReset;
waitfor delay '00:00:00.100';
-- Execute the SP again.
insert into #HHGTTU ( NextVogonPoetryReading )
execute Arthur #TheAnswer = #SixTimesNine Output;
end;
Aside: My apologies for the trauma introduced into your life by my mention of a DataReader. I was merely attempting to pass on my experience in a C# application without getting into the weeds of exactly what sort of connection to the database you are using, which driver(s) might be involved, ... .
I have a SP with an Output parameter that looks like:
ALTER PROCEDURE [dbo].[SP_Name] #VarName decimal(18,2) OUTPUT as ...
I call that procedure from vb.net to get the value for calculations. My problem is: I have 8 SP's with the following structure:
CREATE PROCEDURE [dbo].[SP_Name] #VarName decimal(18,2) OUTPUT as ...
CREATE TABLE #TempTable
Begin
Select ...
End
SET #VarName = Result
But the TempTable is always the same. No I am looking for a way to get all 8 values with only one stored procedure. My idea:
CREATE PROCEDURE [dbo].[SP_Name] #VarName decimal(18,2) OUTPUT as ...
CREATE TABLE #TempTable
---Get first value
Begin
Select ...
End
SET #VarName1 = Result
---Get second value
Begin
Select ...
End
SET #VarName2 = Result
...
How do i have to rewrite the line: ALTER PROCEDURE [dbo].[SP_Name] #VarName decimal(18,2) OUTPUT ir can I even work with an array?
You can use a single stored procedure with all your queries in it. Following will return a single row result set with eight fields and you can grab them from your code using the specific filed name or index.
CREATE PROCEDURE [dbo].[SP_Name]
#VarName decimal(18,2)
AS
BEGIN
DECLARE #VarName1 Datatype, #VarName2 Datatype, ...#VarName8 Datatype
SELECT #VarName1 = yourCol
FROM --First query
SELECT #VarName2 = yourCol
FROM --Second query
...
SELECT #VarName8 = yourCol
FROM --Eighth query
--Finally Select all the variables
SELECT #VarName1 Col1, #VarName2 Col2, ...,#VarName8 Col8
END
OR if you are looking to return results of your all 8 queries, that is also possible. Simply do your select queries in a single stored procedure and grab the DATASET from your code and you can access individual table using zero based Index (ex DataTable1 = YourDataSet.Tables[0])
I have a postgresql function / stored proc that does the following:
1. calls another function and saves the value into a variable.
2. executes another sql statement using the value I got from step one as an argument.
My problem is that the query is not returning any data. No errors are returned either.
I'm just new to postgresql so I don't know the best way to debug... but I added a RAISE NOTICE command right after step 1, like so:
SELECT INTO active_id get_widget_id(widget_desc);
RAISE NOTICE 'Active ID is:(%)', active_id;
In the "Messages" section of the pgadmin3 screen, I see the debug message with the data:
NOTICE: Active ID is:(2)
I'm wondering whether or not the brackets are causing the problem for me.
Here's the sql I'm trying to run in step 2:
SELECT d.id, d.contact_id, d.priority, cp.contact
FROM widget_details d, contact_profile cp, contact_type ct
WHERE d.rule_id=active_id
AND d.active_yn = 't'
AND cp.id=d.contact_id
AND cp.contact_type_id=ct.id
AND ct.name = 'email'
Order by d.priority ASC
You'll notice that in my where clause I am referencing the variable "active_id".
I know that this query should return at least one row because when i run a straight sql select (vs using this function) and substitute the value 2 for the variable "active_id", I get back the data I'm looking for.
Any suggetions would be appreciated.
Thanks.
EDIT 1:
Here's the full function definition:
CREATE TYPE custom_return_type AS (
widgetnum integer,
contactid integer,
priority integer,
contactdetails character varying
);
CREATE OR REPLACE FUNCTION test(widget_desc integer)
RETURNS SETOF custom_return_type AS
$BODY$
DECLARE
active_id integer;
rec custom_return_type ;
BEGIN
SELECT INTO active_id get_widget_id(widget_desc);
RAISE NOTICE 'Active ID is:(%)', active_id;
FOR rec IN
SELECT d.id, d.contact_id, d.priority, cp.contact
FROM widget_details d, contact_profile cp, contact_type ct
WHERE d.rule_id=active_id
AND d.active_yn = 't'
AND cp.id=d.contact_id
AND cp.contact_type_id=ct.id
AND ct.name = 'email'
Order by d.priority ASC
LOOP
RETURN NEXT rec;
END LOOP;
END
$BODY$
That's several levels of too-complicated (edit: as it turns out that Erwin already explained to you last time you posted the same thing). Start by using RETURNS TABLE and RETURN QUERY:
CREATE OR REPLACE FUNCTION test(fmfm_number integer)
RETURNS TABLE (
widgetnum integer,
contactid integer,
priority integer,
contactdetails character varying
) AS
$BODY$
BEGIN
RETURN QUERY SELECT d.id, d.contact_id, d.priority, cp.contact
FROM widget_details d, contact_profile cp, contact_type ct
WHERE d.rule_id = get_widget_id(widget_desc)
AND d.active_yn = 't'
AND cp.id=d.contact_id
AND cp.contact_type_id=ct.id
AND ct.name = 'email'
Order by d.priority ASC;
END
$BODY$ LANGUAGE plpgsql;
at which point it's probably simple enough to be turned into a trivial SQL function or even a view. Hard to be sure, since the function doesn't make tons of sense as written:
You never use the parameter fmfm_number anywhere; and
widget_desc is never defined
so this function could never run. Clearly you haven't shown us the real source code, but some kind of "simplified" code that doesn't match the code you're really having issues with.
There is a difference between:
SELECT INTO ...
[http://www.postgresql.org/docs/current/interactive/sql-selectinto.html]
and
SELECT select_expressions INTO [STRICT] target FROM ...;
[http://www.postgresql.org/docs/current/interactive/plpgsql-statements.html#PLPGSQL-STATEMENTS-SQL-ONEROW]
I think you want:
SELECT get_widget_id(widget_desc) INTO active_id;
I am getting comma separated value like this in a variable (let say variable name #listobj)
'abc' , 'xyz'
but when I am using below statement it is not giving me the correct result
SELECT * FROM someTable
Where column1 IN (#listobj)
but abc is present in the table.
Where I am doing it wrong?
create a function that split the string to
CREATE FUNCTION dbo.Split(#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
then make call to the function SELECT * FROM someTable
Where column1 IN (dbo.Split(#listobj))
enter link description here
SQLFiddle demo
select * from someTable
where ','+LTRIM(RTRIM(#listobj))+',' LIKE '%,'+LTRIM(RTRIM(column1))+',%'
A classic question and the answer is no, you cannot use a parameter in this way. There are several workarounds though
One of which is to parse the value inside the stored procedure and dynamically generate sql inside the procedure to be execute later. However, this is not a good practice.
Refer to this question
How to pass a comma separated list to a stored procedure?
and also some good discussion on it here
http://social.msdn.microsoft.com/Forums/en-US/sqlintegrationservices/thread/1ccdd39e-8d58-45b2-9c21-5c4dbd857f95/
I'm working in a client's SQL Server 2005 database and I'm seeing some odd behavior. I have a little experience with 2008, but this is my first battle with 2005, so I'm hoping this turns out to be something simple related to my inexperience.
What am I trying to do? I am developing a sales dashboard in SSRS 2005. One of the report parameters is a multi-value parameter which I need to pass to a stored procedure. I found a work around in some blogs, as well as here on SO, and implemented a function which splits a comma delimited varchar, and returns a table. I need this function to return the correct values from within the procedure, since I am calling stored procedures for the SSRS datasets.
My Problem: When I execute the function directly from within SSMS for a given value, I receive a table with 7 rows (as expected). When I execute a procedure that calls that function, with the same values passed, it only returns 1 row in the table. The user executing the function and the procedure is also the owner of both objects.
I've done a lot of homework to get to this point prior to posting the question, so I hope I've done my due diligence here. The client hasn't given me privileges to work with SQL Profiler, so I haven't been able to dig in that direction.
I thought perhaps this could be a permissions issue, but the fact that the function is still executed and does return 1 row instead of 7 confused me. It appears to only return the first number from the comma dilimitted string.
My Question: What the heck is causing the behavior outlined below? Please let me know if I should provide any additional information.
Executed from SSMS:
declare
#SiteID varchar(max);
set #SiteID = '1,2,3,4,5,6,7';
BEGIN
exec usp_function_test #SiteID;
select * from udf_rpt_multivalueparamtotable(#SiteID,',',1)
END
Output from Procedure:
Val
---
1
Output from select statement:
Val
---
1
2
3
4
5
6
7
Function Code:
CREATE FUNCTION [dbo].[udf_RPT_MultiValueParamToTAble](
#String VARCHAR(max), /* input string */
#Delimeter char(1), /* delimiter */
#TrimSpace bit ) /* kill whitespace? */
RETURNS #Table TABLE ( [Val] VARCHAR(4000) )
AS
BEGIN
DECLARE #Val VARCHAR(4000)
WHILE LEN(#String) > 0
BEGIN
SET #Val = LEFT(#String,
ISNULL(NULLIF(CHARINDEX(#Delimeter, #String) - 1, -1),
LEN(#String)))
SET #String = SUBSTRING(#String,
ISNULL(NULLIF(CHARINDEX(#Delimeter, #String), 0),
LEN(#String)) + 1, LEN(#String))
IF #TrimSpace = 1 Set #Val = LTRIM(RTRIM(#Val))
INSERT INTO #Table ( [Val] )
VALUES ( #Val )
END
RETURN
END
Test Procedure Code:
CREATE PROCEDURE usp_function_test (#SiteID varchar)
AS
SET NOCOUNT ON
BEGIN
select * from udf_rpt_multivalueparamtotable(#SiteID,',',1)
END
Try defining a scale for the SP parameter -
CREATE PROCEDURE usp_function_test (#SiteID varchar(max))
When you declare a varchar parameter without a scale, it defaults to a scale of 1 - your input value was being silently truncated.
Reference here - http://msdn.microsoft.com/en-us/library/ms176089.aspx