Using a UDF multiple times in a Sproc - tsql

I have a SQL 2k5 sproc I'm working with.
I need to reference a UDF to calculate price based on a few variables and the users permissions. I originally tried this, but it didn't work because I wasn't referencing a field...
SELECT dbo.f_GetPrice(model,userid,authType) 'YourPrice', name, description
FROM tblRL_Products
WHERE 'YourPrice' Between #fromPrice AND #toPrice
OR 'YourPrice' IS NULL
So I modified this to
SELECT dbo.f_GetPrice(model,userid,authType) 'YourPrice', name, description
FROM tblRL_Products
WHERE dbo.f_GetPrice(model,userid,authType) Between #fromPrice AND #toPrice
OR dbo.f_GetPrice(model,userid,authType) IS NULL
When SQL executes this sproc, is it running the function 3X's for each record or does it run it the one time and use the values in the other two places per row.
Is there a more efficient way of doing this?
Edit
This is the Scalar UDF. It needs to grab a price based on the type the user is authorized for, then once we have the right price we need to do a calculation on it. This is all stored in the authorization tables. Every user has an authorization for each line of products. So they may have different price types and calculations for each line, returning back dozens or even hundreds of lines in a single search result call.
In the above code I used authType, that was an old call, we don't use that parameter anymore.
ALTER function [dbo].[f_GetPrice]
(
#model uniqueidentifier,
#userID uniqueidentifier
)
returns money
as
begin
Declare #yourPrice money
WITH ProductPrice AS(
SELECT (CASE PriceType
WHEN 'msrp' THEN p.price_msrp
WHEN 'jobber' THEN p.price_jobber
WHEN 'warehouse' THEN p.price_warehouse
WHEN 'margin' THEN p.price_margin
WHEN 'mycost' THEN p.price_mycost
WHEN 'customprice1' THEN p.price_custom1
WHEN 'customprice2' THEN p.price_custom2
WHEN 'customprice3' THEN p.price_custom2
ELSE p.price_msrp
END) as YourPrice, aup.calc, aup.amount
FROM products p
JOIN lines l ON l.lineID=l.lineID
JOIN authorizations a ON l.authlineID=a.authlineID
JOIN authorizationusers au ON a.auID=au.auID
JOIN authorizationuserprices aup ON au.aupID=aup.aupID
WHERE au.userID=#userID AND p.modelID=#model)
SELECT #yourPrice=(CASE calc
WHEN 'amount' THEN YourPrice+amount
WHEN 'percent' THEN YourPrice+(YourPrice*amount/100)
WHEN 'divide' THEN YourPrice/amount
WHEN 'factore' THEN YourPrice*amount
WHEN 'none' THEN YourPrice
ELSE YourPrice
END) FROM ProductPrice
return #yourPrice
END

If you must use a udf for this, then use a subquery and filter outside the subquery:
select YourPrice, name, description
from
(
SELECT dbo.f_GetPrice(model,userid,authType) YourPrice, name, description
FROM tblRL_Products
) d
WHERE YourPrice Between #fromPrice AND #toPrice
OR YourPrice IS NULL
Then you are only calling your udf once instead of 3 times.

Scalar functions are not good, when they have to be applied to a quite number of rows. In this case definitely you can convert your scalar function into table-valued function, which will not be called one time for every row of input data.
create function [dbo].[ft_GetPrice]
(
#model uniqueidentifier,
#userID uniqueidentifier
)
returns table
as return
(
WITH ProductPrice AS (
SELECT (CASE PriceType
WHEN 'msrp' THEN p.price_msrp
WHEN 'jobber' THEN p.price_jobber
WHEN 'warehouse' THEN p.price_warehouse
WHEN 'margin' THEN p.price_margin
WHEN 'mycost' THEN p.price_mycost
WHEN 'customprice1' THEN p.price_custom1
WHEN 'customprice2' THEN p.price_custom2
WHEN 'customprice3' THEN p.price_custom2
ELSE p.price_msrp
END) as YourPrice, aup.calc, aup.amount
FROM products p
JOIN lines l ON l.lineID=l.lineID
JOIN authorizations a ON l.authlineID=a.authlineID
JOIN authorizationusers au ON a.auID=au.auID
JOIN authorizationuserprices aup ON au.aupID=aup.aupID
WHERE au.userID=#userID AND p.modelID=#model
)
SELECT
CASE calc
WHEN 'amount' THEN YourPrice+amount
WHEN 'percent' THEN YourPrice+(YourPrice*amount/100)
WHEN 'divide' THEN YourPrice/amount
WHEN 'factore' THEN YourPrice*amount
WHEN 'none' THEN YourPrice
ELSE YourPrice
END as YourPrice
FROM ProductPrice
)
GO
Which can be used then as:
SELECT fp.YourPrice, name, description
FROM tblRL_Products
outer apply dbo.ft_GetPrice(model, userid, authType) fp
WHERE fp.YourPrice Between #fromPrice AND #toPrice OR fp.YourPrice IS NULL

Related

Converting rows to columns using crosstab in PostgreSQL not working (relation “table” does not exist)

I need to use the compiled data using CTE and then convert the columns to rows using crosstab(open to other ideas) in the next select statement. Below is the query.
with checked_adgroup AS (
SELECT
ua.new_adgroup,
ua.account,
ua.campaign,
ua.ad_group,
ua."position",
cp.category,
pt.full_value,
FROM unnest_adgroup ua
LEFT JOIN taxonomy_category cp ON ua."position" = cp."position"
LEFT JOIN taxonomy pt ON ua.short_val = pt.short_value AND cp.category = pt.category AND (pt.lob IS NULL OR pt.lob = ua.lob)
)
SELECT *
from crosstab(
'select
cad.account,
cad.campaign,
cad.ad_group,
cad.category,
cad.full_value
FROM checked_adgroup cad
WHERE cad.all_correct AND cad.category IS NOT NULL
ORDER BY 1,2,3')
AS final_result(
account text, campaign text, ad_group text,
division text, lob text, match_type text );
Error message:
ERROR: relation "checked_adgroup" does not exist LINE 7: FROM checked_adgroup cad
Output of checked_adgroup cte looks like below:
enter image description here
Desired output of the final statement is:
enter image description here
Welcome to the community. First off please do not post images, they are useless to work with, and in some instances they are prohibited and cannot be viewed. Instead use formatted text.
I haven't used crosstab functionality all that much, but it does offer a second version which contains 2 queries, the second feeding into the first. There are however a couple errors in your posted query that would need correcting either way. So that first. Look for --<< tag.
with checked_adgroup AS (
SELECT
ua.new_adgroup,
ua.account,
ua.campaign,
ua.ad_group,
ua."position",
cp.category,
pt.full_value,
--<< missing column or ending , above should not be there, assumption missing column see below.
FROM unnest_adgroup ua
LEFT JOIN taxonomy_category cp ON ua."position" = cp."position"
LEFT JOIN taxonomy pt ON ua.short_val = pt.short_value AND cp.category = pt.category AND (pt.lob IS NULL OR pt.lob = ua.lob)
)
SELECT *
from crosstab(
'select
cad.account,
cad.campaign,
cad.ad_group,
cad.category,
cad.full_value
FROM checked_adgroup cad
WHERE cad.all_correct AND cad.category IS NOT NULL
--<< above line has 2 errors:
--<< Incorrectly formatted needs to cad.all_correct is not null AND cad.category IS NOT NULL
--<< column cad.all_correct does not exist (see missing column above
ORDER BY 1,2,3')
AS final_result(
account text, campaign text, ad_group text,
division text, lob text, match_type text);
Now we need to transform the CTE to a second query that crosstab might be ale to use. I have identified each with Postgres $Quoting$, not so much from necessity as standard string quote (') would be sufficant, but more from visibility standing.
select *
from crosstab(
$ct1$select
account,
campaign,
ad_group,
category,
full_value
--<< from checked_adgroup cad
--<< where cad.all_correct and cad.category is not null
--<< moved above lines to second query to avoid reference and removed qualification
order by 1,2,3
$ct1$
, $ct2$select *
from (
select
ua.new_adgroup,
ua.account,
ua.campaign,
ua.ad_group,
ua."position",
cp.category,
pt.full_value,
'mssing from orig posted query' all_correct
from unnest_adgroup ua
left join taxonomy_category cp on ua."position" = cp."position"
left join taxonomy pt on ua.short_val = pt.short_value
and cp.category = pt.category
and (pt.lob is null or pt.lob = ua.lob)
) s
where all_correct is not null and cad.category is not null
--<< move from query1
$ct2$ )
as final_result(
account text, campaign text, ad_group text,
division text, lob text, match_type text );
But at this point I get an error relationship unnest_adgroup does not exist. Which is true as you did not post the definition, not other referenced tables. But that seems to imply the syntax is correct.
Admittedly, this may be way off base if so, so be it, I can always delete later. But, I stuck at home with no other projects at the moment and this seems like an interesting question. Looking forward to the results. Good Luck.

understanding complex SP in DB2

I need to make changes to an SP which has a bunch of complex XML functions and what not
Declare ResultCsr2 Cursor For
WITH
MDI_BOM_COMP(PROD_ID,SITE_ID, xml ) AS (
SELECT TC401F.T41PID,TC401F.T41SID,
XMLSERIALIZE(
XMLAGG(
XMLELEMENT( NAME "MDI_BOM_COMP",
XMLFOREST(
trim(TC401F.T41CTY) AS COMPONENT_TYPE,
TC401F.T41LNO AS COMP_NUM,
trim(TC401F.T41CTO) AS CTRY_OF_ORIGIN,
trim(TC401F.T41DSC) AS DESCRIPTION,
TC401F.T41EFR AS EFFECTIVE_FROM,
TC401F.T41EFT AS EFFECTIVE_TO,
trim(TC401F.T41MID) AS MANUFACTURER_ID,
trim(TC401F.T41MOC) AS MANUFACTURER_ORG_CODE,
trim(TC401F.T41CNO) AS PROD_ID,
trim(TC401F.T41POC) AS PROD_ORG_CODE,
TC401F.T41QPR AS QTY_PER,
trim(TC401F.T41SBI) AS SUB_BOM_ID,
trim(TC401F.T41SBO) AS SUB_BOM_ORG_CODE, --ADB01
trim(TC401F.T41VID) AS SUPPLIER_ID,
trim(TC401F.T41SOC) AS SUPPLIER_ORG_CODE,
TC401F.T41UCT AS UNIT_COST
)
)
) AS CLOB(1M)
)
FROM TC401F TC401F
GROUP BY T41PID,T41SID
)
SELECT
RowNum, '<BOM_INBOUND>' ||
XMLSERIALIZE (
XMLELEMENT(NAME "INTEGRATION_MESSAGE_CONTROL",
XMLFOREST(
'FULL_UPDATE' as ACTION,
'POLARIS' as COMPANY_CODE,
TRIM(TC400F.T40OCD) as ORG_CODE,
'5' as PRIORITY,
'INBOUND_ENTITY_INTEGRATION' as MESSAGE_TYPE,
'POLARIS_INTEGRATION' as USERID,
'TA' as RECEIVER,
HEX(Generate_Unique()) as SOURCE_SYSTEM_TOKEN
),
XMLELEMENT(NAME "BUS_KEY",
XMLFOREST(
TRIM(TC400F.T40BID) as BOM_ID,
TRIM(TC400F.T40OCD) as ORG_CODE
)
)
) AS VARCHAR(1000)
)
|| '<MDI_BOM>' ||
XMLSERIALIZE (
XMLFOREST(
TRIM(TC400F.T40ATP) AS ASSEMBLY_TYPE,
TRIM(TC400F.T40BID) AS BOM_ID,
TRIM(TC400F.T40CCD) AS CURRENCY_CODE,
TC400F.T40DPC AS DIRECT_PROCESSING_COST,
TC400F.T40EFD AS EFFECTIVE_FROM,
TC400F.T40EFT AS EFFECTIVE_TO,
TRIM(TC400F.T40MID) AS MANUFACTURER_ID,
TRIM(TC400F.T40MOC) AS MANUFACTURER_ORG_CODE,
TRIM(TC400F.T40OCD) AS ORG_CODE,
TRIM(TC400F.T40PRF) AS PROD_FAMILY,
TRIM(TC400F.T40PID) AS PROD_ID,
TRIM(TC400F.T40POC) AS PROD_ORG_CODE,
TRIM(TC400F.T40ISA) AS IS_ACTIVE,
TRIM(TC400F.T40VID) AS SUPPLIER_ID,
TRIM(TC400F.T40SOC) AS SUPPLIER_ORG_CODE,
TRIM(TC400F.T40PSF) AS PROD_SUB_FAMILY,
CASE TRIM(TC400F.T40PML)
WHEN '' THEN TRIM(TC400F.T40PML)
ELSE TRIM(TC400F.T40PML) || '~' || TRIM(TC403F.T43MDD)
END AS PROD_MODEL
) AS VARCHAR(3000)
)
|| IFNULL(MBC.xml, '') ||
XMLSERIALIZE (
XMLFOREST(
XMLFOREST(
TRIM(TC400F.T40CCD) AS CURRENCY_CODE,
TC400F.T40PRI AS PRICE,
TRIM(TC400F.T40PTY) AS PRICE_TYPE
) AS MDI_BOM_PRICE,
XMLFOREST(
TRIM(TC400F.T40CCD) AS CURRENCY_CODE,
TRIM(TC400F.T40PRI) AS PRICE,
'TRANSACTION_VALUE' AS PRICE_TYPE
) AS MDI_BOM_PRICE,
XMLFOREST(
TRIM(TC400F.T40INA) AS INCLUDE_IN_AVERAGING
) AS MDI_BOM_IMPL_BOM_PROD_FAMILY_AUTOMOBILES
) AS VARCHAR(3000)
)
|| '</MDI_BOM>' ||
'</BOM_INBOUND>' XML
FROM (
SELECT
ROW_NUMBER() OVER (
ORDER BY T40STS
,T40SID
,T40BID
) AS RowNum
,t.*
FROM TC400F t
) TC400F
LEFT OUTER JOIN MDI_BOM_COMP MBC
ON TC400F.T40SID = MBC.SITE_ID
AND TC400F.T40PID = MBC.PROD_ID
LEFT OUTER JOIN TC403F TC403F
ON TC400F.T40PML <> ''
AND TC400F.T40PML = TC403F.T43MDL
WHERE TC400F.T40STS = '10'
AND TC400F.RowNUM BETWEEN
(P_STARTROW + (P_PAGENOS - 1) * P_NBROFRCDS)
AND (P_STARTROW + (P_PAGENOS - 1) * P_NBROFRCDS +
P_NBROFRCDS - 1);
Given above is a cursor declaration in the SP code which I am struggling to understand. The very first WITH itself seems to be mysterious. I have used it along with temporary table names but this is the first time, Im seeing something of this sort which seems to be an SP or UDF? Can someone please guide me on how to understand and make sense out of all this?
Adding further to the question, the actual requirement here is to arrange the data in the XML such a way that that those records which have TC401F.T41SBI field populated should appear in the beginning of the XML output..
This field is being selected as below in the code:
trim(TC401F.T41SBI) AS SUB_BOM_ID. If this field is non-blank, this should appear first in the XML and any records with this field value Blank should appear only after. What would be the best approach to do this? Using ORDER BY in any way does not really seem to help as the XML is actually created through some functions and ordering by does not affect how the items are arranged within the XML. One approach I could think of was using a where clause where TC401F.T41SBI <> '' first then append those records where TC401F.T41SBI = ''
Best I can do is help with the CTE.
WITH
MDI_BOM_COMP(PROD_ID,SITE_ID, xml ) AS (
SELECT TC401F.T41PID,TC401F.T41SID,
This just generates a table named MDI_BOM_COMP with three columns named PROD_ID, SITE_ID, and XML. The table will have one record for each PROD_ID, SITE_ID, and the contents of XML will be an XML snippet with all the components for that product and site.
Now the XML part can be a bit confusing, but if we break it down into it's scalar and aggregate components, we can make it a bit more understandable.
First ignore the grouping. so the CTE retrieves each row in TC401F. XMLELEMENT and XMLFORREST are scalar functions. XMLELEMENT creates a single XML element The tag is the first parameter, and the content of the element is the second in the above example. XMLFORREST is like a bunch of XMLELEMENTs concatenated together.
XMLSERIALIZE(
XMLAGG(
XMLELEMENT( NAME "MDI_BOM_COMP",
XMLFOREST(
trim(TC401F.T41CTY) AS COMPONENT_TYPE,
TC401F.T41LNO AS COMP_NUM,
trim(TC401F.T41CTO) AS CTRY_OF_ORIGIN,
trim(TC401F.T41DSC) AS DESCRIPTION,
TC401F.T41EFR AS EFFECTIVE_FROM,
TC401F.T41EFT AS EFFECTIVE_TO,
trim(TC401F.T41MID) AS MANUFACTURER_ID,
trim(TC401F.T41MOC) AS MANUFACTURER_ORG_CODE,
trim(TC401F.T41CNO) AS PROD_ID,
trim(TC401F.T41POC) AS PROD_ORG_CODE,
TC401F.T41QPR AS QTY_PER,
trim(TC401F.T41SBI) AS SUB_BOM_ID,
trim(TC401F.T41SBO) AS SUB_BOM_ORG_CODE, --ADB01
trim(TC401F.T41VID) AS SUPPLIER_ID,
trim(TC401F.T41SOC) AS SUPPLIER_ORG_CODE,
TC401F.T41UCT AS UNIT_COST
)
)
) AS CLOB(1M)
So in the example, for each row in the table, XMLFORREST creates a list of XML elements, one for each of COMPONENT_TYPE, COMP_NUM, CTRY_OF_ORIGIN, etc. These elements form the content of another XML element MDI_BOM_COMP which is created by XMLELEMENT.
Now for each row in the table we have selected PROD_ID, SITE_ID, and created some XML. Next we group by PROD_ID, and SITE_ID. The aggregation function XMLAGG will collect all the XML for each PROD_ID and SITE_ID, and concatenate it together.
Finally XMLSERIALIZE will convert the internal XML representation to the string format we all know and love ;)
I think I found the answer for my requirement. I had to add an order by field name after XMLELEMENT function

Joining with set-returning function (SRF) and access columns in SQLAlchemy

Suppose I have an activity table and a subscription table. Each activity has an array of generic references to some other object, and each subscription has a single generic reference to some other object in the same set.
CREATE TABLE activity (
id serial primary key,
ob_refs UUID[] not null
);
CREATE TABLE subscription (
id UUID primary key,
ob_ref UUID,
subscribed boolean not null
);
I want to join with the set-returning function unnest so I can find the "deepest" matching subscription, something like this:
SELECT id
FROM (
SELECT DISTINCT ON (activity.id)
activity.id,
x.ob_ref, x.ob_depth,
subscription.subscribed IS NULL OR subscription.subscribed = TRUE
AS subscribed,
FROM activity
LEFT JOIN subscription
ON activity.ob_refs #> array[subscription.ob_ref]
LEFT JOIN unnest(activity.ob_refs)
WITH ORDINALITY AS x(ob_ref, ob_depth)
ON subscription.ob_ref = x.ob_ref
ORDER BY x.ob_depth DESC
) sub
WHERE subscribed = TRUE;
But I can't figure out how to do that second join and get access to the columns. I've tried creating a FromClause like this:
act_ref_t = (sa.select(
[sa.column('unnest', UUID).label('ob_ref'),
sa.column('ordinality', sa.Integer).label('ob_depth')],
from_obj=sa.func.unnest(Activity.ob_refs))
.suffix_with('WITH ORDINALITY')
.alias('act_ref_t'))
...
query = (query
.outerjoin(
act_ref_t,
Subscription.ob_ref == act_ref_t.c.ob_ref))
.order_by(activity.id, act_ref_t.ob_depth)
But that results in this SQL with another subquery:
LEFT OUTER JOIN (
SELECT unnest AS ob_ref, ordinality AS ref_i
FROM unnest(activity.ob_refs) WITH ORDINALITY
) AS act_ref_t
ON subscription.ob_refs #> ARRAY[act_ref_t.ob_ref]
... which fails because of the missing and unsupported LATERAL keyword:
There is an entry for table "activity", but it cannot be referenced from this part of the query.
So, how can I create a JOIN clause for this SRF without using a subquery? Or is there something else I'm missing?
Edit 1 Using sa.text with TextClause.columns instead of sa.select gets me a lot closer:
act_ref_t = (sa.sql.text(
"unnest(activity.ob_refs) WITH ORDINALITY")
.columns(sa.column('unnest', UUID),
sa.column('ordinality', sa.Integer))
.alias('act_ref'))
But the resulting SQL fails because it wraps the clause in parentheses:
LEFT OUTER JOIN (unnest(activity.ob_refs) WITH ORDINALITY)
AS act_ref ON subscription.ob_ref = act_ref.unnest
The error is syntax error at or near ")". Can I get TextAsFrom to not be wrapped in parentheses?
It turns out this is not directly supported by SA, but the correct behaviour can be achieved with a ColumnClause and a FunctionElement. First import this recipe as described by zzzeek in this SA issue. Then create a special unnest function that includes the WITH ORDINALITY modifier:
class unnest_func(ColumnFunction):
name = 'unnest'
column_names = ['unnest', 'ordinality']
#compiles(unnest_func)
def _compile_unnest_func(element, compiler, **kw):
return compiler.visit_function(element, **kw) + " WITH ORDINALITY"
You can then use it in joins, ordering, etc. like this:
act_ref = unnest_func(Activity.ob_refs)
query = (query
.add_columns(act_ref.c.unnest, act_ref.c.ordinality)
.outerjoin(act_ref, sa.true())
.outerjoin(Subscription, Subscription.ob_ref == act_ref.c.unnest)
.order_by(act_ref.c.ordinality.desc()))

Is there a JDFTVAL equivalent for SQL?

For Iseries/IBMi DB2.
I am joining multiple files/tables together.
I have written the code in both DDS and SQL.
The DDS Logical File is working exactly as expected, but I can not use it for embedded sql in rpgle as it then defaults to the SQE engine resulting in horrendous performance.
The SQL view, on the other hand had NULLs until I used IFNULL( MBRDESCR, ''). But now MBRDECSR is a VARCHAR. Which is unacceptable.
So how do I create a SQL join without NULLs and VARCHARs?
Requested Sample Code:
DDS:
JDFTVAL
R TRANSR JFILE(TRANSPF MBRPF)
J JOIN(1 2)
JFLD(MBRID MBRID)
*
TRANSID JREF(1)
MBRID JREF(1)
MBRNAME JREF(2)
MBRSURNME JREF(2)
*
K TRANSID
K MBRID
SQL:
CREATE VIEW TRANSV01 AS (
SELECT TRANSID ,
MBRID ,
CAST(IFNULL(MBRNAME , '') as Char(20)) ,
CAST(IFNULL(MBRSURNME, '') as Char(25))
FROM TRANSPF
--Member Name
LEFT OUTER JOIN MBRPF on MBRID = MBRID
) RCDFMT TRANSR;
Please note the following:
Example above is simplified
Not every MBRID in the TRANSPF has a corresponding entry in the MBRPF (ie. no referential constraint). Thus when MBRPF is joined to the TRANSPF, there will be NULL values in MBRNAME, MBRSURNME. Unless JDFTVAL or IFNULL() is used.
I prefer not to have a VARCHAR, because of performance and extname() in rpgle.
I prefer not to have NULL values, I do not want the pgm to have to handle them.
Assuming it's the 'Allows the null value' that you find undesirable, use a UNION. The first SELECT chooses all the rows that match, which will set the NOT NULL property for you. The second SELECT chooses all the rows that don't have a match - you provide filler fields for those.
CREATE VIEW TRANSV01 AS (
SELECT TRANSID ,
MBRID ,
MBRNAME ,
MBRSURNME
FROM TRANSPF
--Member Name
JOIN MBRPF on MBRID = MBRID
UNION
SELECT TRANSID ,
MBRID ,
CAST('') as Char(20)) ,
CAST('') as Char(25))
FROM TRANSPF
--Member Name
EXCEPTION JOIN MBRPF on MBRID = MBRID
) RCDFMT TRANSR;

TSQL CTE Error: Incorrect syntax near ')'

I am developing a TSQL stored proc using SSMS 2008 and am receiving the above error while generating a CTE. I want to add logic to this SP to return every day, not just the days with data. How do I do this? Here is my SP so far:
ALTER Proc [dbo].[rpt_rd_CensusWithChart]
#program uniqueidentifier = NULL,
#office uniqueidentifier = NULL
AS
DECLARE #a_date datetime
SET #a_date = case when MONTH(GETDATE()) >= 7 THEN '7/1/' + CAST(YEAR(GETDATE()) AS VARCHAR(30))
ELSE '7/1/' + CAST(YEAR(GETDATE())-1 AS VARCHAR(30)) END
if exists (
select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#ENROLLEES')
) DROP TABLE #ENROLLEES;
if exists (
select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#DISCHARGES')
) DROP TABLE #DISCHARGES;
declare #sum_enrollment int
set #sum_enrollment =
(select sum(1)
from enrollment_view A
join enrollment_info_expanded_view C on A.enrollment_id = C.enroll_el_id
where
(#office is NULL OR A.group_profile_id = #office)
AND (#program is NULL OR A.program_info_id = #program)
and (C.pe_end_date IS NULL OR C.pe_end_date > #a_date)
AND C.pe_start_date IS NOT NULL and C.pe_start_date < #a_date)
select
A.program_info_id as [Program code],
A.[program_name],
A.profile_name as Facility,
A.group_profile_id as Facility_code,
A.people_id,
1 as enrollment_id,
C.pe_start_date,
C.pe_end_date,
LEFT(datename(month,(C.pe_start_date)),3) as a_month,
day(C.pe_start_date) as a_day,
#sum_enrollment as sum_enrollment
into #ENROLLEES
from enrollment_view A
join enrollment_info_expanded_view C on A.enrollment_id = C.enroll_el_id
where
(#office is NULL OR A.group_profile_id = #office)
AND (#program is NULL OR A.program_info_id = #program)
and (C.pe_end_date IS NULL OR C.pe_end_date > #a_date)
AND C.pe_start_date IS NOT NULL and C.pe_start_date >= #a_date
;WITH #ENROLLEES AS (
SELECT '7/1/11' AS dt
UNION ALL
SELECT DATEADD(d, 1, pe_start_date) as dt
FROM #ENROLLEES s
WHERE DATEADD(d, 1, pe_start_date) <= '12/1/11')
The most obvious issue (and probably the one that causes the error message too) is the absence of the actual statement to which the last CTE is supposed to pertain. I presume it should be a SELECT statement, one that would combine the result set of the CTE with the data from the #ENROLLEES table.
And that's where another issue emerges.
You see, apart from the fact that a name that starts with a single # is hardly advisable for anything that is not a local temporary table (a CTE is not a table indeed), you've also chosen for your CTE a particular name that already belongs to an existing table (more precisely, to the already mentioned #ENROLLEES temporary table), and the one you are going to pull data from too. You should definitely not use an existing table's name for a CTE, or you will not be able to join it with the CTE due to the name conflict.
It also appears that, based on its code, the last CTE represents an unfinished implementation of the logic you say you want to add to the SP. I can suggest some idea, but before I go on I'd like you to realise that there are actually two different requests in your post. One is about finding the cause of the error message, the other is about code for a new logic. Generally you are probably better off separating such requests into distinct questions, and so you might be in this case as well.
Anyway, here's my suggestion:
build a complete list of dates you want to be accounted for in the result set (that's what the CTE will be used for);
left-join that list with the #ENROLLEES table to pick data for the existing dates and some defaults or NULLs for the non-existing ones.
It might be implemented like this:
… /* all your code up until the last WITH */
;
WITH cte AS (
SELECT CAST('7/1/11' AS date) AS dt
UNION ALL
SELECT DATEADD(d, 1, dt) as dt
FROM cte
WHERE dt < '12/1/11'
)
SELECT
cte.dt,
tmp.[Program code],
tmp.[program_name],
… /* other columns as necessary; you might also consider
enveloping some or all of the "tmp" columns in ISNULLs,
like in
ISNULL(tmp.[Program code], '(none)') AS [Program code]
to provide default values for absent data */
FROM cte
LEFT JOIN #ENROLLEES tmp ON cte.dt = tmp.pe_start_date
;